* [PATCH v3 0/4] BCM283x Camera Receiver driver
@ 2017-09-20 16:07 Dave Stevenson
[not found] ` <cover.1505916622.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
` (3 more replies)
0 siblings, 4 replies; 30+ messages in thread
From: Dave Stevenson @ 2017-09-20 16:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
devicetree
Cc: Dave Stevenson
Hi All.
v3 of this patch set.
This addresses the DT issues raised by Rob, and the things raised by
Eric on the driver. More complete change details between v2 and v3
are in the individual patches.
Thanks
Dave
Dave Stevenson (4):
[media] v4l2-common: Add helper function for fourcc to string
[media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
[media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
MAINTAINERS: Add entry for BCM2835 camera driver
.../devicetree/bindings/media/bcm2835-unicam.txt | 85 +
MAINTAINERS | 7 +
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/bcm2835/Kconfig | 14 +
drivers/media/platform/bcm2835/Makefile | 3 +
drivers/media/platform/bcm2835/bcm2835-unicam.c | 2192 ++++++++++++++++++++
drivers/media/platform/bcm2835/vc4-regs-unicam.h | 264 +++
drivers/media/v4l2-core/v4l2-common.c | 18 +
include/media/v4l2-common.h | 3 +
10 files changed, 2588 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] 30+ messages in thread
* [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
2017-09-20 16:07 [PATCH v3 0/4] BCM283x Camera Receiver driver Dave Stevenson
@ 2017-09-20 16:07 ` Dave Stevenson
2017-09-20 16:07 ` [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
` (2 subsequent siblings)
3 siblings, 0 replies; 30+ messages in thread
From: Dave Stevenson @ 2017-09-20 16:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
Stefan Wahren, Sakari Ailus, linux-media-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Dave Stevenson
New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
that converts a fourcc into a nice printable version.
Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
---
No changes from v2 to v3
drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
include/media/v4l2-common.h | 3 +++
2 files changed, 21 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index a5ea1f5..0219895 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
}
EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
+
+char *v4l2_fourcc2s(u32 fourcc, char *buf)
+{
+ buf[0] = fourcc & 0x7f;
+ buf[1] = (fourcc >> 8) & 0x7f;
+ buf[2] = (fourcc >> 16) & 0x7f;
+ buf[3] = (fourcc >> 24) & 0x7f;
+ if (fourcc & (1 << 31)) {
+ buf[4] = '-';
+ buf[5] = 'B';
+ buf[6] = 'E';
+ buf[7] = '\0';
+ } else {
+ buf[4] = '\0';
+ }
+ return buf;
+}
+EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index aac8b7b..5b0fff9 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
void v4l2_get_timestamp(struct timeval *tv);
+#define V4L2_FOURCC_MAX_SIZE 8
+char *v4l2_fourcc2s(u32 fourcc, char *buf);
+
#endif /* V4L2_COMMON_H_ */
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
@ 2017-09-20 16:07 ` Dave Stevenson
0 siblings, 0 replies; 30+ messages in thread
From: Dave Stevenson @ 2017-09-20 16:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
devicetree
Cc: Dave Stevenson
New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
that converts a fourcc into a nice printable version.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
No changes from v2 to v3
drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
include/media/v4l2-common.h | 3 +++
2 files changed, 21 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index a5ea1f5..0219895 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
}
EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
+
+char *v4l2_fourcc2s(u32 fourcc, char *buf)
+{
+ buf[0] = fourcc & 0x7f;
+ buf[1] = (fourcc >> 8) & 0x7f;
+ buf[2] = (fourcc >> 16) & 0x7f;
+ buf[3] = (fourcc >> 24) & 0x7f;
+ if (fourcc & (1 << 31)) {
+ buf[4] = '-';
+ buf[5] = 'B';
+ buf[6] = 'E';
+ buf[7] = '\0';
+ } else {
+ buf[4] = '\0';
+ }
+ return buf;
+}
+EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index aac8b7b..5b0fff9 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
void v4l2_get_timestamp(struct timeval *tv);
+#define V4L2_FOURCC_MAX_SIZE 8
+char *v4l2_fourcc2s(u32 fourcc, char *buf);
+
#endif /* V4L2_COMMON_H_ */
--
2.7.4
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-09-20 16:07 [PATCH v3 0/4] BCM283x Camera Receiver driver Dave Stevenson
[not found] ` <cover.1505916622.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
@ 2017-09-20 16:07 ` Dave Stevenson
[not found] ` <fae3d29bba67825030c0077dd9c79534b6888512.1505916622.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
2017-09-20 16:07 ` [PATCH v3 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
2017-09-20 16:07 ` [PATCH v3 4/4] MAINTAINERS: Add entry for BCM2835 camera driver Dave Stevenson
3 siblings, 1 reply; 30+ messages in thread
From: Dave Stevenson @ 2017-09-20 16:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
devicetree
Cc: Dave Stevenson
Document the DT bindings for the CSI2/CCP2 receiver peripheral
(known as Unicam) on BCM283x SoCs.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
Changes since v2
- Removed all references to Linux drivers.
- Reworded section about disabling the firmware driver.
- Renamed clock from "lp_clock" to "lp" in description and example.
- Referred to video-interfaces.txt and stated requirements on remote-endpoint
and data-lanes.
- Corrected typo in example from csi to csi1.
- Removed unnecessary #address-cells and #size-cells in example.
- Removed setting of status from the example.
.../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
1 file changed, 85 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..7714fb3
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
@@ -0,0 +1,85 @@
+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.
+
+The main platform using this SoC is the Raspberry Pi family of boards.
+On the Pi the VideoCore firmware can also control this hardware block,
+and driving it from two different processors will cause issues.
+To avoid this, the firmware checks the device tree configuration
+during boot. If it finds device tree nodes called csi0 or csi1 then
+it will stop the firmware accessing the block, and it can then
+safely be used via the device tree binding.
+
+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" entry, matching entries in the
+ clocks property.
+
+Unicam supports a single port node. It should contain one 'port' child node
+with child 'endpoint' node. Please refer to the bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Within the endpoint node the "remote-endpoint" and "data-lanes" properties
+are mandatory.
+Data lane reordering is not supported so the data lanes must be in order,
+starting at 1. The number of data lanes should represent the number of
+usable lanes for the hardware block. That may be limited by either the SoC or
+how the platform presents the interface, and the lower value must be used.
+
+Lane reordering is not supported on the clock lane either, so the optional
+property "clock-lane" will implicitly be <0>.
+Similarly lane inversion is not supported, therefore "lane-polarities" will
+implicitly be <0 0 0 0 0>.
+Neither of these values will be checked.
+
+Example:
+ csi1: csi1@7e801000 {
+ compatible = "brcm,bcm2835-unicam";
+ reg = <0x7e801000 0x800>,
+ <0x7e802004 0x4>;
+ interrupts = <2 7>;
+ clocks = <&clocks BCM2835_CLOCK_CAM1>;
+ clock-names = "lp";
+
+ port {
+ csi1_ep: endpoint {
+ remote-endpoint = <&tc358743_0>;
+ data-lanes = <1 2>;
+ };
+ };
+ };
+
+ i2c0: i2c@7e205000 {
+ tc358743: csi-hdmi-bridge@0f {
+ compatible = "toshiba,tc358743";
+ reg = <0x0f>;
+
+ clocks = <&tc358743_clk>;
+ clock-names = "refclk";
+
+ tc358743_clk: bridge-clk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <27000000>;
+ };
+
+ port {
+ tc358743_0: endpoint {
+ remote-endpoint = <&csi1_ep>;
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+ clock-noncontinuous;
+ link-frequencies =
+ /bits/ 64 <297000000>;
+ };
+ };
+ };
+ };
--
2.7.4
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v3 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
2017-09-20 16:07 [PATCH v3 0/4] BCM283x Camera Receiver driver Dave Stevenson
[not found] ` <cover.1505916622.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
2017-09-20 16:07 ` [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
@ 2017-09-20 16:07 ` Dave Stevenson
2017-09-22 10:37 ` Hans Verkuil
2017-09-20 16:07 ` [PATCH v3 4/4] MAINTAINERS: Add entry for BCM2835 camera driver Dave Stevenson
3 siblings, 1 reply; 30+ messages in thread
From: Dave Stevenson @ 2017-09-20 16:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
devicetree
Cc: Dave Stevenson
Add driver for the Unicam camera receiver block on
BCM283x processors.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
Changes from v2.
- Don't store the irq value as it isn't used outside the probe.
- Change PIX_FMT_ALL_ defines to avoid potential clashes with 4CCs.
- Clock now called "lp" and not "lp_clock".
- Minor description changes.
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/bcm2835/Kconfig | 14 +
drivers/media/platform/bcm2835/Makefile | 3 +
drivers/media/platform/bcm2835/bcm2835-unicam.c | 2192 ++++++++++++++++++++++
drivers/media/platform/bcm2835/vc4-regs-unicam.h | 264 +++
6 files changed, 2475 insertions(+)
create mode 100644 drivers/media/platform/bcm2835/Kconfig
create mode 100644 drivers/media/platform/bcm2835/Makefile
create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c
create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 7e7cc49..1e5f004 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -150,6 +150,7 @@ source "drivers/media/platform/am437x/Kconfig"
source "drivers/media/platform/xilinx/Kconfig"
source "drivers/media/platform/rcar-vin/Kconfig"
source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/bcm2835/Kconfig"
config VIDEO_TI_CAL
tristate "TI CAL (Camera Adaptation Layer) driver"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index c1ef946..045de3f 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -90,3 +90,4 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/
obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
obj-y += meson/
+obj-y += bcm2835/
diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig
new file mode 100644
index 0000000..6a74842
--- /dev/null
+++ b/drivers/media/platform/bcm2835/Kconfig
@@ -0,0 +1,14 @@
+# Broadcom VideoCore4 V4L2 camera support
+
+config VIDEO_BCM2835_UNICAM
+ tristate "Broadcom BCM2835 Unicam video capture driver"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ ---help---
+ Say Y here to enable V4L2 subdevice for CSI2 receiver.
+ This is a V4L2 subdevice that interfaces directly to the VC4 peripheral.
+
+ To compile this driver as a module, choose M here. The module
+ will be called bcm2835-unicam.
diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile
new file mode 100644
index 0000000..a98aba0
--- /dev/null
+++ b/drivers/media/platform/bcm2835/Makefile
@@ -0,0 +1,3 @@
+# Makefile for BCM2835 Unicam driver
+
+obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o
diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
new file mode 100644
index 0000000..93831fb
--- /dev/null
+++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
@@ -0,0 +1,2192 @@
+/*
+ * BCM2835 Unicam capture Driver
+ *
+ * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
+ *
+ * Dave Stevenson <dave.stevenson@raspberrypi.org>
+ *
+ * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
+ * TI CAL camera interface driver by Benoit Parrot.
+ *
+ *
+ * There are two camera drivers in the kernel for BCM283x - this one
+ * and bcm2835-camera (currently in staging).
+ *
+ * This driver directly controls the Unicam peripheral - there is no
+ * involvement with the VideoCore firmware. Unicam receives CSI-2 or
+ * CCP2 data and writes it into SDRAM. The only processing options are
+ * to repack Bayer data into an alternate format, and applying windowing
+ * (currently not implemented).
+ * It should be possible to connect it to any sensor with a
+ * suitable output interface and V4L2 subdevice driver.
+ *
+ * bcm2835-camera uses the VideoCore firmware to control the sensor,
+ * Unicam, ISP, and all tuner control loops. Fully processed frames are
+ * delivered to the driver by the firmware. It only has sensor drivers
+ * for Omnivision OV5647, and Sony IMX219 sensors.
+ *
+ * The two drivers are mutually exclusive for the same Unicam instance.
+ * The VideoCore firmware checks the device tree configuration during boot.
+ * If it finds device tree nodes called csi0 or csi1 it will block the
+ * firmware from accessing the peripheral, and bcm2835-camera will
+ * not be able to stream data.
+ *
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vc4-regs-unicam.h"
+
+#define UNICAM_MODULE_NAME "unicam"
+#define UNICAM_VERSION "0.1.0"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-3");
+
+#define unicam_dbg(level, dev, fmt, arg...) \
+ v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg)
+#define unicam_info(dev, fmt, arg...) \
+ v4l2_info(&(dev)->v4l2_dev, fmt, ##arg)
+#define unicam_err(dev, fmt, arg...) \
+ v4l2_err(&(dev)->v4l2_dev, fmt, ##arg)
+
+/*
+ * Stride is a 16 bit register, but also has to be a multiple of 16.
+ */
+#define BPL_ALIGNMENT 16
+#define MAX_BYTESPERLINE ((1 << 16) - BPL_ALIGNMENT)
+/*
+ * Max width is therefore determined by the max stride divided by
+ * the number of bits per pixel. Take 32bpp as a
+ * worst case.
+ * No imposed limit on the height, so adopt a square image for want
+ * of anything better.
+ */
+#define MAX_WIDTH (MAX_BYTESPERLINE / 4)
+#define MAX_HEIGHT MAX_WIDTH
+/* Define a nominal minimum image size */
+#define MIN_WIDTH 16
+#define MIN_HEIGHT 16
+/*
+ * Whilst Unicam doesn't require any additional padding on the image
+ * height, various other parts of the BCM283x frameworks require a multiple
+ * of 16.
+ * Seeing as image buffers are significantly larger than this extra
+ * padding, add it in order to simplify integration.
+ */
+#define HEIGHT_ALIGNMENT 16
+
+/*
+ * struct unicam_fmt - Unicam media bus format information
+ * @pixelformat: V4L2 pixel format FCC identifier.
+ * @code: V4L2 media bus format code.
+ * @depth: Bits per pixel (when stored in memory).
+ * @csi_dt: CSI data type.
+ */
+struct unicam_fmt {
+ u32 fourcc;
+ u32 code;
+ u8 depth;
+ u8 csi_dt;
+};
+
+/*
+ * The peripheral can unpack and repack between several of
+ * the Bayer raw formats, so any Bayer format can be advertised
+ * as the same Bayer order in each of the supported bit depths.
+ * These values are used as flags to denote that all Bayer formats
+ * in the specified order should be advertised. They are not
+ * used outside of this driver but must avoid collisions with
+ * the standard V4L2_PIX_FMT_ values, hence 0xFFFFFFxx.
+ */
+#define PIX_FMT_ALL_BGGR 0xFFFFFFF0
+#define PIX_FMT_ALL_RGGB 0xFFFFFFF1
+#define PIX_FMT_ALL_GBRG 0xFFFFFFF2
+#define PIX_FMT_ALL_GRBG 0xFFFFFFF3
+
+static const struct unicam_fmt formats[] = {
+ /* YUV Formats */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .depth = 16,
+ .csi_dt = 0x1e,
+ }, {
+ /* RGB Formats */
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .depth = 16,
+ .csi_dt = 0x22,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .depth = 16,
+ .csi_dt = 0x22
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .depth = 16,
+ .csi_dt = 0x21,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .depth = 16,
+ .csi_dt = 0x21,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .depth = 24,
+ .csi_dt = 0x24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .depth = 24,
+ .csi_dt = 0x24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .depth = 32,
+ .csi_dt = 0x0,
+ }, {
+ /* Bayer Formats */
+ .fourcc = PIX_FMT_ALL_BGGR,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
+ }, {
+ .fourcc = PIX_FMT_ALL_GBRG,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
+ }, {
+ .fourcc = PIX_FMT_ALL_GRBG,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
+ }, {
+ .fourcc = PIX_FMT_ALL_RGGB,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
+ }, {
+ .fourcc = PIX_FMT_ALL_BGGR,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 10,
+ .csi_dt = 0x2b,
+ }, {
+ .fourcc = PIX_FMT_ALL_GBRG,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 10,
+ .csi_dt = 0x2b,
+ }, {
+ .fourcc = PIX_FMT_ALL_GRBG,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 10,
+ .csi_dt = 0x2b,
+ }, {
+ .fourcc = PIX_FMT_ALL_RGGB,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 10,
+ .csi_dt = 0x2b,
+ }, {
+ .fourcc = PIX_FMT_ALL_BGGR,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 12,
+ .csi_dt = 0x2c,
+ }, {
+ .fourcc = PIX_FMT_ALL_GBRG,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 12,
+ .csi_dt = 0x2c,
+ }, {
+ .fourcc = PIX_FMT_ALL_GRBG,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 12,
+ .csi_dt = 0x2c,
+ }, {
+ .fourcc = PIX_FMT_ALL_RGGB,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 12,
+ .csi_dt = 0x2c,
+ }, {
+ .fourcc = PIX_FMT_ALL_BGGR,
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .depth = 14,
+ .csi_dt = 0x2d,
+ }, {
+ .fourcc = PIX_FMT_ALL_GBRG,
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .depth = 14,
+ .csi_dt = 0x2d,
+ }, {
+ .fourcc = PIX_FMT_ALL_GRBG,
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .depth = 14,
+ .csi_dt = 0x2d,
+ }, {
+ .fourcc = PIX_FMT_ALL_RGGB,
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .depth = 14,
+ .csi_dt = 0x2d,
+ },
+};
+
+struct bayer_fmt {
+ u32 fourcc;
+ u8 depth;
+};
+
+const struct bayer_fmt all_bayer_bggr[] = {
+ {V4L2_PIX_FMT_SBGGR8, 8},
+ {V4L2_PIX_FMT_SBGGR10P, 10},
+ {V4L2_PIX_FMT_SBGGR12, 12},
+ {V4L2_PIX_FMT_SBGGR16, 16},
+ {0, 0}
+};
+
+const struct bayer_fmt all_bayer_rggb[] = {
+ {V4L2_PIX_FMT_SRGGB8, 8},
+ {V4L2_PIX_FMT_SRGGB10P, 10},
+ {V4L2_PIX_FMT_SRGGB12, 12},
+ {V4L2_PIX_FMT_SRGGB16, 16},
+ {0, 0}
+};
+
+const struct bayer_fmt all_bayer_gbrg[] = {
+ {V4L2_PIX_FMT_SGBRG8, 8},
+ {V4L2_PIX_FMT_SGBRG10P, 10},
+ {V4L2_PIX_FMT_SGBRG12, 12},
+ {V4L2_PIX_FMT_SGBRG16, 16},
+ {0, 0}
+};
+
+const struct bayer_fmt all_bayer_grbg[] = {
+ {V4L2_PIX_FMT_SGRBG8, 8},
+ {V4L2_PIX_FMT_SGRBG10P, 10},
+ {V4L2_PIX_FMT_SGRBG12, 12},
+ {V4L2_PIX_FMT_SGRBG16, 16},
+ {0, 0}
+};
+
+struct unicam_dmaqueue {
+ struct list_head active;
+};
+
+struct unicam_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct unicam_cfg {
+ /* peripheral base address */
+ void __iomem *base;
+ /* clock gating base address */
+ void __iomem *clk_gate_base;
+};
+
+#define MAX_POSSIBLE_FMTS \
+ (ARRAY_SIZE(formats) + \
+ ARRAY_SIZE(all_bayer_bggr) + \
+ ARRAY_SIZE(all_bayer_rggb) + \
+ ARRAY_SIZE(all_bayer_grbg) + \
+ ARRAY_SIZE(all_bayer_gbrg))
+
+struct unicam_device {
+ /* V4l2 specific parameters */
+ /* Identifies video device for this channel */
+ struct video_device video_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct v4l2_fwnode_endpoint endpoint;
+
+ struct v4l2_async_subdev asd;
+
+ /* unicam cfg */
+ struct unicam_cfg cfg;
+ /* clock handle */
+ struct clk *clock;
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ /* parent device */
+ struct platform_device *pdev;
+ /* subdevice async Notifier */
+ struct v4l2_async_notifier notifier;
+ unsigned int sequence;
+
+ /* ptr to sub device */
+ struct v4l2_subdev *sensor;
+ /* Pad config for the sensor */
+ struct v4l2_subdev_pad_config *sensor_config;
+ /* current input at the sub device */
+ int current_input;
+
+ /* Pointer pointing to current v4l2_buffer */
+ struct unicam_buffer *cur_frm;
+ /* Pointer pointing to next v4l2_buffer */
+ struct unicam_buffer *next_frm;
+
+ /* video capture */
+ const struct unicam_fmt *fmt;
+ /* Used to store current pixel format */
+ struct v4l2_format v_fmt;
+ /* Used to store current mbus frame format */
+ struct v4l2_mbus_framefmt m_fmt;
+
+ struct unicam_fmt active_fmts[MAX_POSSIBLE_FMTS];
+ int num_active_fmt;
+ unsigned int virtual_channel;
+ enum v4l2_mbus_type bus_type;
+ /*
+ * Stores bus.mipi_csi2.flags for CSI2 sensors, or
+ * bus.mipi_csi1.strobe for CCP2.
+ */
+ unsigned int bus_flags;
+ unsigned int max_data_lanes;
+ unsigned int active_data_lanes;
+
+ struct v4l2_rect crop;
+
+ /* Currently selected input on subdev */
+ int input;
+
+ /* Buffer queue used in video-buf */
+ struct vb2_queue buffer_queue;
+ /* Queue of filled frames */
+ struct unicam_dmaqueue dma_queue;
+ /* IRQ lock for DMA queue */
+ spinlock_t dma_queue_lock;
+ /* lock used to access this structure */
+ struct mutex lock;
+ /* Flag to denote that we are processing buffers */
+ int streaming;
+};
+
+/* Hardware access */
+#define clk_write(dev, val) writel((val) | 0x5a000000, (dev)->clk_gate_base)
+#define clk_read(dev) readl((dev)->clk_gate_base)
+
+#define reg_read(dev, offset) readl((dev)->base + (offset))
+#define reg_write(dev, offset, val) writel(val, (dev)->base + (offset))
+
+#define reg_read_field(dev, offset, mask) get_field(reg_read((dev), (offset), \
+ mask))
+
+static inline int get_field(u32 value, u32 mask)
+{
+ return (value & mask) >> __ffs(mask);
+}
+
+static inline void set_field(u32 *valp, u32 field, u32 mask)
+{
+ u32 val = *valp;
+
+ val &= ~mask;
+ val |= (field << __ffs(mask)) & mask;
+ *valp = val;
+}
+
+static inline void reg_write_field(struct unicam_cfg *dev, u32 offset,
+ u32 field, u32 mask)
+{
+ u32 val = reg_read((dev), (offset));
+
+ set_field(&val, field, mask);
+ reg_write((dev), (offset), val);
+}
+
+/* Power management functions */
+static inline int unicam_runtime_get(struct unicam_device *dev)
+{
+ int r;
+
+ r = pm_runtime_get_sync(&dev->pdev->dev);
+
+ return r;
+}
+
+static inline void unicam_runtime_put(struct unicam_device *dev)
+{
+ pm_runtime_put_sync(&dev->pdev->dev);
+}
+
+/* Format setup functions */
+static int find_mbus_depth_by_code(u32 code)
+{
+ const struct unicam_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ARRAY_SIZE(formats); k++) {
+ fmt = &formats[k];
+ if (fmt->code == code)
+ return fmt->depth;
+ }
+
+ return 0;
+}
+
+static const struct unicam_fmt *find_format_by_code(struct unicam_device *dev,
+ u32 code)
+{
+ const struct unicam_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < dev->num_active_fmt; k++) {
+ fmt = &dev->active_fmts[k];
+ if (fmt->code == code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev,
+ u32 pixelformat)
+{
+ const struct unicam_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < dev->num_active_fmt; k++) {
+ fmt = &dev->active_fmts[k];
+ if (fmt->fourcc == pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static void dump_active_formats(struct unicam_device *dev)
+{
+ int i;
+ char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+ for (i = 0; i < dev->num_active_fmt; i++) {
+ unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04X, fourcc %s, depth %d\n",
+ i, &dev->active_fmts[i], dev->active_fmts[i].code,
+ v4l2_fourcc2s(dev->active_fmts[i].fourcc,
+ fourcc_str),
+ dev->active_fmts[i].depth);
+ }
+}
+
+static inline unsigned int bytes_per_line(u32 width,
+ const struct unicam_fmt *fmt)
+{
+ return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT);
+}
+
+static int __subdev_get_format(struct unicam_device *dev,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt = {0};
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+
+ ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_config,
+ &sd_fmt);
+ if (ret < 0)
+ return ret;
+
+ if (mbus_fmt->field != V4L2_FIELD_NONE) {
+ /*
+ * No support for receiving interlaced video, so try to
+ * disable it if reported.
+ */
+ mbus_fmt->field = V4L2_FIELD_NONE;
+ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
+ dev->sensor_config,
+ &sd_fmt);
+ }
+ *fmt = *mbus_fmt;
+
+ unicam_dbg(1, dev, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int __subdev_set_format(struct unicam_device *dev,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ *mbus_fmt = *fmt;
+
+ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
+ &sd_fmt);
+ if (ret < 0)
+ return ret;
+
+ unicam_dbg(1, dev, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int unicam_calc_format_size_bpl(struct unicam_device *dev,
+ const struct unicam_fmt *fmt,
+ struct v4l2_format *f)
+{
+ unsigned int min_bytesperline;
+ char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+ if (!fmt) {
+ unicam_dbg(3, dev, "No unicam_fmt provided!\n");
+ return -EINVAL;
+ }
+
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
+ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0,
+ 0);
+
+ min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt);
+
+ if (f->fmt.pix.bytesperline > min_bytesperline &&
+ f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
+ f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
+ BPL_ALIGNMENT);
+ else
+ f->fmt.pix.bytesperline = min_bytesperline;
+
+ /* Align height up for compatibility with other hardware blocks */
+ f->fmt.pix.sizeimage = ALIGN(f->fmt.pix.height, HEIGHT_ALIGNMENT) *
+ f->fmt.pix.bytesperline;
+
+ unicam_dbg(3, dev, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
+ __func__, v4l2_fourcc2s(f->fmt.pix.pixelformat, fourcc_str),
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+static int unicam_reset_format(struct unicam_device *dev)
+{
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ ret = __subdev_get_format(dev, &mbus_fmt);
+ if (ret) {
+ unicam_err(dev, "Failed to get_format - ret %d\n", ret);
+ return ret;
+ }
+
+ if (mbus_fmt.code != dev->fmt->code) {
+ unicam_err(dev, "code mismatch - fmt->code %08X, mbus_fmt.code %08X\n",
+ dev->fmt->code, mbus_fmt.code);
+ return ret;
+ }
+
+ v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
+ dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ unicam_calc_format_size_bpl(dev, dev->fmt, &dev->v_fmt);
+
+ dev->m_fmt = mbus_fmt;
+
+ return 0;
+}
+
+static void unicam_wr_dma_addr(struct unicam_device *dev, unsigned int dmaaddr)
+{
+ unicam_dbg(1, dev, "wr_dma_addr %08X-%08X\n",
+ dmaaddr, dmaaddr + dev->v_fmt.fmt.pix.sizeimage);
+ reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
+ reg_write(&dev->cfg,
+ UNICAM_IBEA0,
+ dmaaddr + dev->v_fmt.fmt.pix.sizeimage);
+ reg_write(&dev->cfg, UNICAM_DBSA0, (uint32_t)dmaaddr);
+ reg_write(&dev->cfg, UNICAM_DBEA0, (uint32_t)dmaaddr + (16 << 10));
+}
+
+static inline void unicam_schedule_next_buffer(struct unicam_device *dev)
+{
+ struct unicam_dmaqueue *dma_q = &dev->dma_queue;
+ struct unicam_buffer *buf;
+ unsigned long addr;
+
+ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+ dev->next_frm = buf;
+ list_del(&buf->list);
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ unicam_wr_dma_addr(dev, addr);
+}
+
+static inline void unicam_process_buffer_complete(struct unicam_device *dev)
+{
+ dev->cur_frm->vb.field = dev->m_fmt.field;
+ dev->cur_frm->vb.sequence = dev->sequence++;
+
+ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ dev->cur_frm = dev->next_frm;
+}
+
+/*
+ * unicam_isr : ISR handler for unicam capture
+ * @irq: irq number
+ * @dev_id: dev_id ptr
+ *
+ * It changes status of the captured buffer, takes next buffer from the queue
+ * and sets its address in unicam registers
+ */
+static irqreturn_t unicam_isr(int irq, void *dev)
+{
+ struct unicam_device *unicam = (struct unicam_device *)dev;
+ int ista, sta;
+ struct unicam_cfg *cfg = &unicam->cfg;
+ struct unicam_dmaqueue *dma_q = &unicam->dma_queue;
+
+ /*
+ * Don't service interrupts if not streaming.
+ * Avoids issues if the VPU should enable the
+ * peripheral without the kernel knowing (that
+ * shouldn't happen, but causes issues if it does).
+ */
+ if (!unicam->streaming)
+ return IRQ_HANDLED;
+
+ sta = reg_read(cfg, UNICAM_STA);
+ /* Write value back to clear the interrupts */
+ reg_write(cfg, UNICAM_STA, sta);
+
+ ista = reg_read(cfg, UNICAM_ISTA);
+ /* Write value back to clear the interrupts */
+ reg_write(cfg, UNICAM_ISTA, ista);
+
+ if (!(sta && (UNICAM_IS | UNICAM_PI0)))
+ return IRQ_HANDLED;
+
+ if (ista & UNICAM_FSI) {
+ /*
+ * Timestamp is to be when the first data byte was captured,
+ * aka frame start.
+ */
+ if (unicam->cur_frm)
+ unicam->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
+ }
+ if (ista & UNICAM_FEI || sta & UNICAM_PI0) {
+ /*
+ * Ensure we have swapped buffers already as we can't
+ * stop the peripheral. Overwrite the frame we've just
+ * captured instead.
+ */
+ if (unicam->cur_frm &&
+ unicam->cur_frm != unicam->next_frm)
+ unicam_process_buffer_complete(unicam);
+ }
+
+ if (ista & (UNICAM_FSI | UNICAM_LCI)) {
+ spin_lock(&unicam->dma_queue_lock);
+ if (!list_empty(&dma_q->active) &&
+ unicam->cur_frm == unicam->next_frm)
+ unicam_schedule_next_buffer(unicam);
+ spin_unlock(&unicam->dma_queue_lock);
+ }
+
+ if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) {
+ /* Switch out of trigger mode if selected */
+ reg_write_field(&unicam->cfg, UNICAM_ICTL, 1, UNICAM_TFC);
+ reg_write_field(&unicam->cfg, UNICAM_ICTL, 0, UNICAM_FCM);
+ }
+ return IRQ_HANDLED;
+}
+
+static int unicam_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
+
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev->v4l2_dev.name);
+
+ return 0;
+}
+
+static int unicam_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct unicam_device *dev = video_drvdata(file);
+ const struct unicam_fmt *fmt = NULL;
+
+ if (f->index >= dev->num_active_fmt)
+ return -EINVAL;
+
+ fmt = &dev->active_fmts[f->index];
+
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int unicam_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ *f = dev->v_fmt;
+
+ return 0;
+}
+
+static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_device *dev = video_drvdata(file);
+ const struct unicam_fmt *fmt;
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
+ if (!fmt) {
+ unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use default of %08X\n",
+ f->fmt.pix.pixelformat, dev->active_fmts[0].fourcc);
+
+ /* Just get the first one enumerated */
+ fmt = &dev->active_fmts[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
+
+ v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
+ /*
+ * No support for receiving interlaced video, so never
+ * request it from the sensor subdev.
+ */
+ mbus_fmt->field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
+ &sd_fmt);
+ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ if (mbus_fmt->field != V4L2_FIELD_NONE)
+ unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
+
+ v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
+ /*
+ * Use current colorspace for now, it will get
+ * updated properly during s_fmt
+ */
+ f->fmt.pix.colorspace = dev->v_fmt.fmt.pix.colorspace;
+ return unicam_calc_format_size_bpl(dev, fmt, f);
+}
+
+static int unicam_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_device *dev = video_drvdata(file);
+ struct vb2_queue *q = &dev->buffer_queue;
+ const struct unicam_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt = {0};
+ int ret;
+ char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ ret = unicam_try_fmt_vid_cap(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
+ if (!fmt) {
+ /* Unknown pixel format - adopt a default */
+ fmt = &dev->active_fmts[0];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ return -EINVAL;
+ }
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+ ret = __subdev_set_format(dev, &mbus_fmt);
+ if (ret) {
+ unicam_dbg(3, dev,
+ "%s __subdev_set_format failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Just double check nothing has gone wrong */
+ if (mbus_fmt.code != fmt->code) {
+ unicam_dbg(3, dev,
+ "%s subdev changed format on us, this should not happen\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ dev->fmt = fmt;
+ dev->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+ dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
+ unicam_reset_format(dev);
+
+ unicam_dbg(3, dev,
+ "%s %dx%d, mbus_fmt %08X, V4L2 pix %s.\n",
+ __func__,
+ dev->v_fmt.fmt.pix.width,
+ dev->v_fmt.fmt.pix.height,
+ mbus_fmt.code,
+ v4l2_fourcc2s(dev->v_fmt.fmt.pix.pixelformat,
+ fourcc_str));
+
+ *f = dev->v_fmt;
+
+ return 0;
+}
+
+static int unicam_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct unicam_device *dev = vb2_get_drv_priv(vq);
+ unsigned int size = dev->v_fmt.fmt.pix.sizeimage;
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+
+ if (*nplanes) {
+ if (sizes[0] < size) {
+ unicam_err(dev, "sizes[0] %i < size %u\n",
+ sizes[0], size);
+ return -EINVAL;
+ }
+ size = sizes[0];
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int unicam_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
+ vb.vb2_buf);
+ unsigned long size;
+
+ if (WARN_ON(!dev->fmt))
+ return -EINVAL;
+
+ size = dev->v_fmt.fmt.pix.sizeimage;
+ if (vb2_plane_size(vb, 0) < size) {
+ unicam_err(dev, "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+ return 0;
+}
+
+static void unicam_buffer_queue(struct vb2_buffer *vb)
+{
+ struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
+ vb.vb2_buf);
+ struct unicam_dmaqueue *dma_queue = &dev->dma_queue;
+ unsigned long flags = 0;
+
+ /* recheck locking */
+ spin_lock_irqsave(&dev->dma_queue_lock, flags);
+ list_add_tail(&buf->list, &dma_queue->active);
+ spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
+}
+
+static void unicam_wr_dma_config(struct unicam_device *dev,
+ unsigned int stride)
+{
+ reg_write(&dev->cfg, UNICAM_IBLS, stride);
+}
+
+static void unicam_set_packing_config(struct unicam_device *dev)
+{
+ int pack, unpack;
+ u32 val;
+ int mbus_depth = find_mbus_depth_by_code(dev->fmt->code);
+ int v4l2_depth = dev->fmt->depth;
+
+ if (mbus_depth == v4l2_depth) {
+ unpack = UNICAM_PUM_NONE;
+ pack = UNICAM_PPM_NONE;
+ } else {
+ switch (mbus_depth) {
+ case 8:
+ unpack = UNICAM_PUM_UNPACK8;
+ break;
+ case 10:
+ unpack = UNICAM_PUM_UNPACK10;
+ break;
+ case 12:
+ unpack = UNICAM_PUM_UNPACK12;
+ break;
+ case 14:
+ unpack = UNICAM_PUM_UNPACK14;
+ break;
+ case 16:
+ unpack = UNICAM_PUM_UNPACK16;
+ break;
+ default:
+ unpack = UNICAM_PUM_NONE;
+ break;
+ }
+ switch (v4l2_depth) {
+ case 8:
+ pack = UNICAM_PPM_PACK8;
+ break;
+ case 10:
+ pack = UNICAM_PPM_PACK10;
+ break;
+ case 12:
+ pack = UNICAM_PPM_PACK12;
+ break;
+ case 14:
+ pack = UNICAM_PPM_PACK14;
+ break;
+ case 16:
+ pack = UNICAM_PPM_PACK16;
+ break;
+ default:
+ pack = UNICAM_PPM_NONE;
+ break;
+ }
+ }
+
+ val = 0;
+ set_field(&val, 2, UNICAM_DEBL_MASK);
+ set_field(&val, unpack, UNICAM_PUM_MASK);
+ set_field(&val, pack, UNICAM_PPM_MASK);
+ reg_write(&dev->cfg, UNICAM_IPIPE, val);
+}
+
+static void unicam_cfg_image_id(struct unicam_device *dev)
+{
+ struct unicam_cfg *cfg = &dev->cfg;
+
+ if (dev->bus_type == V4L2_MBUS_CSI2) {
+ /* CSI2 mode */
+ reg_write(cfg, UNICAM_IDI0,
+ (dev->virtual_channel << 6) |
+ dev->fmt->csi_dt);
+ } else {
+ /* CCP2 mode */
+ reg_write(cfg, UNICAM_IDI0,
+ (0x80 | dev->fmt->csi_dt));
+ }
+}
+
+void unicam_start_rx(struct unicam_device *dev, unsigned long addr)
+{
+ u32 val;
+ unsigned int i;
+ struct unicam_cfg *cfg = &dev->cfg;
+ int line_int_freq = dev->v_fmt.fmt.pix.height >> 2;
+
+ if (line_int_freq < 128)
+ line_int_freq = 128;
+
+ /* Enable lane clocks */
+ val = 1;
+ for (i = 0; i < dev->active_data_lanes; i++)
+ val = val << 2 | 1;
+ clk_write(cfg, val);
+
+ /* Basic init */
+ reg_write(cfg, UNICAM_CTRL, UNICAM_MEM);
+
+ /* Enable analogue control, and leave in reset. */
+ val = UNICAM_AR;
+ set_field(&val, 7, UNICAM_CTATADJ_MASK);
+ set_field(&val, 7, UNICAM_PTATADJ_MASK);
+ reg_write(cfg, UNICAM_ANA, val);
+ usleep_range(1000, 2000);
+
+ /* Come out of reset */
+ reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_AR);
+
+ /* Peripheral reset */
+ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
+ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
+
+ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
+
+ /* Enable Rx control. */
+ val = reg_read(cfg, UNICAM_CTRL);
+ if (dev->bus_type == V4L2_MBUS_CSI2) {
+ set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
+ set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK);
+ } else {
+ set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK);
+ set_field(&val, dev->bus_flags, UNICAM_DCM_MASK);
+ }
+ set_field(&val, 0xF, UNICAM_PFT_MASK);
+ set_field(&val, 128, UNICAM_OET_MASK);
+ reg_write(cfg, UNICAM_CTRL, val);
+
+ reg_write(cfg, UNICAM_IHWIN, 0);
+ reg_write(cfg, UNICAM_IVWIN, 0);
+
+ val = reg_read(&dev->cfg, UNICAM_PRI);
+ set_field(&val, 0, UNICAM_BL_MASK);
+ set_field(&val, 0, UNICAM_BS_MASK);
+ set_field(&val, 0xE, UNICAM_PP_MASK);
+ set_field(&val, 8, UNICAM_NP_MASK);
+ set_field(&val, 2, UNICAM_PT_MASK);
+ set_field(&val, 1, UNICAM_PE);
+ reg_write(cfg, UNICAM_PRI, val);
+
+ reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_DDL);
+
+ /* Always start in trigger frame capture mode (UNICAM_FCM set) */
+ val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM;
+ set_field(&val, line_int_freq, UNICAM_LCIE_MASK);
+ reg_write(cfg, UNICAM_ICTL, val);
+ reg_write(cfg, UNICAM_STA, UNICAM_STA_MASK_ALL);
+ reg_write(cfg, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL);
+
+ /* tclk_term_en */
+ reg_write_field(cfg, UNICAM_CLT, 2, UNICAM_CLT1_MASK);
+ /* tclk_settle */
+ reg_write_field(cfg, UNICAM_CLT, 6, UNICAM_CLT2_MASK);
+ /* td_term_en */
+ reg_write_field(cfg, UNICAM_DLT, 2, UNICAM_DLT1_MASK);
+ /* ths_settle */
+ reg_write_field(cfg, UNICAM_DLT, 6, UNICAM_DLT2_MASK);
+ /* trx_enable */
+ reg_write_field(cfg, UNICAM_DLT, 0, UNICAM_DLT3_MASK);
+
+ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_SOE);
+
+ val = 0;
+ set_field(&val, 1, UNICAM_PCE);
+ set_field(&val, 1, UNICAM_GI);
+ set_field(&val, 1, UNICAM_CPH);
+ set_field(&val, 0, UNICAM_PCVC_MASK);
+ set_field(&val, 1, UNICAM_PCDT_MASK);
+ reg_write(cfg, UNICAM_CMP0, val);
+
+ /* Enable clock lane */
+ val = 0;
+ if (dev->bus_type == V4L2_MBUS_CSI2) {
+ /* CSI2 */
+ set_field(&val, 1, UNICAM_CLE);
+ set_field(&val, 1, UNICAM_CLLPE);
+ if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
+ set_field(&val, 1, UNICAM_CLTRE);
+ set_field(&val, 1, UNICAM_CLHSE);
+ }
+ } else {
+ /* CCP2 */
+ set_field(&val, 1, UNICAM_CLE);
+ set_field(&val, 1, UNICAM_CLHSE);
+ set_field(&val, 1, UNICAM_CLTRE);
+ }
+ reg_write(cfg, UNICAM_CLK, val);
+
+ /* Enable required data lanes */
+ val = 0;
+ if (dev->bus_type == V4L2_MBUS_CSI2) {
+ /* CSI2 */
+ set_field(&val, 1, UNICAM_DLE);
+ set_field(&val, 1, UNICAM_DLLPE);
+ if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
+ set_field(&val, 1, UNICAM_DLTRE);
+ set_field(&val, 1, UNICAM_DLHSE);
+ }
+ } else {
+ /* CCP2 */
+ set_field(&val, 1, UNICAM_DLE);
+ set_field(&val, 1, UNICAM_DLHSE);
+ set_field(&val, 1, UNICAM_DLTRE);
+ }
+ reg_write(cfg, UNICAM_DAT0, val);
+
+ if (dev->active_data_lanes == 1)
+ val = 0;
+ reg_write(cfg, UNICAM_DAT1, val);
+
+ if (dev->max_data_lanes > 2) {
+ if (dev->active_data_lanes == 2)
+ val = 0;
+ reg_write(cfg, UNICAM_DAT2, val);
+
+ if (dev->active_data_lanes == 3)
+ val = 0;
+ reg_write(cfg, UNICAM_DAT3, val);
+ }
+
+ unicam_wr_dma_config(dev, dev->v_fmt.fmt.pix.bytesperline);
+ unicam_wr_dma_addr(dev, addr);
+ unicam_set_packing_config(dev);
+ unicam_cfg_image_id(dev);
+
+ val = 0;
+ set_field(&val, 0, UNICAM_EDL_MASK);
+ reg_write(cfg, UNICAM_DCS, val);
+
+ val = reg_read(cfg, UNICAM_MISC);
+ set_field(&val, 1, UNICAM_FL0);
+ set_field(&val, 1, UNICAM_FL1);
+ reg_write(cfg, UNICAM_MISC, val);
+
+ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE);
+
+ reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
+
+ reg_write_field(cfg, UNICAM_DCS, 1, UNICAM_LDP);
+
+ /*
+ * Enable trigger only for the first frame to
+ * sync correctly to the FS from the source.
+ */
+ reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_TFC);
+}
+
+static void unicam_disable(struct unicam_device *dev)
+{
+ struct unicam_cfg *cfg = &dev->cfg;
+
+ /* Analogue lane control disable */
+ reg_write_field(cfg, UNICAM_ANA, 1, UNICAM_DDL);
+
+ /* Stop the output engine */
+ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_SOE);
+
+ /* Disable the data lanes. */
+ reg_write(cfg, UNICAM_DAT0, 0);
+ reg_write(cfg, UNICAM_DAT1, 0);
+
+ if (dev->max_data_lanes > 2) {
+ reg_write(cfg, UNICAM_DAT2, 0);
+ reg_write(cfg, UNICAM_DAT3, 0);
+ }
+
+ /* Peripheral reset */
+ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
+ usleep_range(50, 100);
+ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
+
+ /* Disable peripheral */
+ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
+
+ /* Disable all lane clocks */
+ clk_write(cfg, 0);
+}
+
+static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct unicam_device *dev = vb2_get_drv_priv(vq);
+ struct unicam_dmaqueue *dma_q = &dev->dma_queue;
+ struct unicam_buffer *buf, *tmp;
+ unsigned long addr = 0;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dev->dma_queue_lock, flags);
+ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+ dev->cur_frm = buf;
+ dev->next_frm = buf;
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
+
+ addr = vb2_dma_contig_plane_dma_addr(&dev->cur_frm->vb.vb2_buf, 0);
+ dev->sequence = 0;
+
+ ret = unicam_runtime_get(dev);
+ if (ret < 0) {
+ unicam_dbg(3, dev, "unicam_runtime_get failed\n");
+ goto err_release_buffers;
+ }
+
+ dev->active_data_lanes = dev->max_data_lanes;
+ if (dev->bus_type == V4L2_MBUS_CSI2 &&
+ v4l2_subdev_has_op(dev->sensor, video, g_mbus_config)) {
+ struct v4l2_mbus_config mbus_config;
+
+ ret = v4l2_subdev_call(dev->sensor, video, g_mbus_config,
+ &mbus_config);
+ if (ret < 0) {
+ unicam_dbg(3, dev, "g_mbus_config failed\n");
+ goto err_pm_put;
+ }
+
+ switch (mbus_config.flags & V4L2_MBUS_CSI2_LANES) {
+ case V4L2_MBUS_CSI2_1_LANE:
+ dev->active_data_lanes = 1;
+ break;
+ case V4L2_MBUS_CSI2_2_LANE:
+ dev->active_data_lanes = 2;
+ break;
+ case V4L2_MBUS_CSI2_3_LANE:
+ dev->active_data_lanes = 3;
+ break;
+ case V4L2_MBUS_CSI2_4_LANE:
+ dev->active_data_lanes = 4;
+ break;
+ default:
+ unicam_err(dev, "Invalid CSI2 lane flag value - %X\n",
+ mbus_config.flags & V4L2_MBUS_CSI2_LANES);
+ break;
+ }
+
+ if ((mbus_config.flags ^ dev->bus_flags) &
+ (V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
+ V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK))
+ unicam_err(dev, "g_mbus_format returned different clocking mode to DT\n");
+ }
+ if (dev->active_data_lanes > dev->max_data_lanes) {
+ unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n",
+ dev->active_data_lanes,
+ dev->max_data_lanes);
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ unicam_dbg(1, dev, "Running with %u data lanes\n",
+ dev->active_data_lanes);
+
+ ret = clk_set_rate(dev->clock, 100 * 1000 * 1000);
+ if (ret) {
+ unicam_err(dev, "failed to set up clock\n");
+ goto err_pm_put;
+ }
+
+ ret = clk_prepare_enable(dev->clock);
+ if (ret) {
+ unicam_err(dev, "Failed to enable CSI clock: %d\n", ret);
+ goto err_pm_put;
+ }
+ ret = v4l2_subdev_call(dev->sensor, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ unicam_err(dev, "power on failed in subdev\n");
+ goto err_clock_unprepare;
+ }
+ dev->streaming = 1;
+
+ unicam_start_rx(dev, addr);
+
+ ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
+ if (ret < 0) {
+ unicam_err(dev, "stream on failed in subdev\n");
+ goto err_disable_unicam;
+ }
+
+ return 0;
+
+err_disable_unicam:
+ unicam_disable(dev);
+ v4l2_subdev_call(dev->sensor, core, s_power, 0);
+err_clock_unprepare:
+ clk_disable_unprepare(dev->clock);
+err_pm_put:
+ unicam_runtime_put(dev);
+err_release_buffers:
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ if (dev->cur_frm != dev->next_frm)
+ vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
+ VB2_BUF_STATE_QUEUED);
+ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ dev->next_frm = NULL;
+ dev->cur_frm = NULL;
+
+ return ret;
+}
+
+static void unicam_stop_streaming(struct vb2_queue *vq)
+{
+ struct unicam_device *dev = vb2_get_drv_priv(vq);
+ struct unicam_dmaqueue *dma_q = &dev->dma_queue;
+ struct unicam_buffer *buf, *tmp;
+ unsigned long flags;
+
+ if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0)
+ unicam_err(dev, "stream off failed in subdev\n");
+
+ unicam_disable(dev);
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&dev->dma_queue_lock, flags);
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ if (dev->cur_frm == dev->next_frm) {
+ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ } else {
+ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+ dev->cur_frm = NULL;
+ dev->next_frm = NULL;
+ spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
+
+ if (v4l2_subdev_has_op(dev->sensor, core, s_power)) {
+ if (v4l2_subdev_call(dev->sensor, core, s_power, 0) < 0)
+ unicam_err(dev, "power off failed in subdev\n");
+ }
+
+ clk_disable_unprepare(dev->clock);
+ unicam_runtime_put(dev);
+}
+
+static int unicam_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) {
+ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+ inp->std = 0;
+ } else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) {
+ inp->capabilities = V4L2_IN_CAP_STD;
+ if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std)
+ < 0)
+ inp->std = V4L2_STD_ALL;
+ } else {
+ inp->capabilities = 0;
+ inp->std = 0;
+ }
+ sprintf(inp->name, "Camera 0");
+ return 0;
+}
+
+static int unicam_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int unicam_s_input(struct file *file, void *priv, unsigned int i)
+{
+ /*
+ * FIXME: Ideally we would like to be able to query the sensor
+ * for information over the input connectors it supports, and
+ * map that through in to a call to video_ops->s_routing.
+ * There is no infrastructure support for defining that within
+ * devicetree at present. Until that is implemented we can't
+ * map a user physical connector number to s_routing input number.
+ */
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int unicam_querystd(struct file *file, void *priv,
+ v4l2_std_id *std)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, video, querystd, std);
+}
+
+static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, video, g_std, std);
+}
+
+static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+ struct unicam_device *dev = video_drvdata(file);
+ int ret;
+ v4l2_std_id current_std;
+
+ ret = v4l2_subdev_call(dev->sensor, video, g_std, ¤t_std);
+ if (ret)
+ return ret;
+
+ if (std == current_std)
+ return 0;
+
+ if (vb2_is_busy(&dev->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
+
+ /* Force recomputation of bytesperline */
+ dev->v_fmt.fmt.pix.bytesperline = 0;
+
+ unicam_reset_format(dev);
+
+ return ret;
+}
+
+static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
+}
+
+static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
+}
+
+static int unicam_g_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
+}
+
+static int unicam_s_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct unicam_device *dev = video_drvdata(file);
+ struct v4l2_dv_timings current_timings;
+ int ret;
+
+ ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings,
+ ¤t_timings);
+
+ if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false))
+ return 0;
+
+ if (vb2_is_busy(&dev->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
+
+ /* Force recomputation of bytesperline */
+ dev->v_fmt.fmt.pix.bytesperline = 0;
+
+ unicam_reset_format(dev);
+
+ return ret;
+}
+
+static int unicam_query_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
+}
+
+static int unicam_enum_dv_timings(struct file *file, void *priv,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
+}
+
+static int unicam_dv_timings_cap(struct file *file, void *priv,
+ struct v4l2_dv_timings_cap *cap)
+{
+ struct unicam_device *dev = video_drvdata(file);
+
+ return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
+}
+
+static int unicam_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_event_subscribe(fh, sub, 4, NULL);
+ }
+
+ return v4l2_ctrl_subscribe_event(fh, sub);
+}
+
+static int unicam_log_status(struct file *file, void *fh)
+{
+ struct unicam_device *dev = video_drvdata(file);
+ /* status for sub devices */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status);
+
+ return 0;
+}
+
+static void unicam_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct unicam_device *dev =
+ container_of(sd->v4l2_dev, struct unicam_device, v4l2_dev);
+
+ switch (notification) {
+ case V4L2_DEVICE_NOTIFY_EVENT:
+ v4l2_event_queue(&dev->video_dev, arg);
+ break;
+ default:
+ break;
+ }
+}
+
+static const struct vb2_ops unicam_video_qops = {
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .queue_setup = unicam_queue_setup,
+ .buf_prepare = unicam_buffer_prepare,
+ .buf_queue = unicam_buffer_queue,
+ .start_streaming = unicam_start_streaming,
+ .stop_streaming = unicam_stop_streaming,
+};
+
+/* unicam capture driver file operations */
+static const struct v4l2_file_operations unicam_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+/* unicam capture ioctl operations */
+static const struct v4l2_ioctl_ops unicam_ioctl_ops = {
+ .vidioc_querycap = unicam_querycap,
+ .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap,
+
+ .vidioc_enum_input = unicam_enum_input,
+ .vidioc_g_input = unicam_g_input,
+ .vidioc_s_input = unicam_s_input,
+
+ .vidioc_querystd = unicam_querystd,
+ .vidioc_s_std = unicam_s_std,
+ .vidioc_g_std = unicam_g_std,
+
+ .vidioc_g_edid = unicam_g_edid,
+ .vidioc_s_edid = unicam_s_edid,
+
+ .vidioc_s_dv_timings = unicam_s_dv_timings,
+ .vidioc_g_dv_timings = unicam_g_dv_timings,
+ .vidioc_query_dv_timings = unicam_query_dv_timings,
+ .vidioc_enum_dv_timings = unicam_enum_dv_timings,
+ .vidioc_dv_timings_cap = unicam_dv_timings_cap,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = unicam_log_status,
+ .vidioc_subscribe_event = unicam_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int
+unicam_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct unicam_device *unicam = container_of(notifier->v4l2_dev,
+ struct unicam_device, v4l2_dev);
+ struct v4l2_subdev_mbus_code_enum mbus_code;
+ int j;
+ struct unicam_fmt *active_fmt;
+ int ret = 0;
+
+ if (unicam->sensor) {
+ unicam_info(unicam, "Rejecting subdev %s (Already set!!)",
+ subdev->name);
+ return 0;
+ }
+
+ unicam->sensor = subdev;
+ unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name);
+
+ /* Enumerate sub device formats and enable all matching local formats */
+ unicam->num_active_fmt = 0;
+ active_fmt = &unicam->active_fmts[0];
+ unicam_dbg(2, unicam, "Get supported formats...\n");
+ for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
+ const struct unicam_fmt *fmt = NULL;
+ int k;
+
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = j;
+ ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+ NULL, &mbus_code);
+ if (ret < 0) {
+ unicam_dbg(2, unicam,
+ "subdev->enum_mbus_code idx %d returned %d - continue\n",
+ j, ret);
+ continue;
+ }
+
+ unicam_dbg(2, unicam,
+ "subdev %s: code: %04x idx: %d\n",
+ subdev->name, mbus_code.code, j);
+
+ for (k = 0; k < ARRAY_SIZE(formats); k++) {
+ if (mbus_code.code == formats[k].code) {
+ fmt = &formats[k];
+ break;
+ }
+ }
+ unicam_dbg(2, unicam, "fmt %04x returned as %p, V4L2 FOURCC %04x, csi_dt %02X\n",
+ mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
+ fmt ? fmt->csi_dt : 0);
+ if (fmt) {
+ const struct bayer_fmt *fmt_list = NULL;
+
+ switch (fmt->fourcc) {
+ case PIX_FMT_ALL_BGGR:
+ fmt_list = all_bayer_bggr;
+ break;
+ case PIX_FMT_ALL_RGGB:
+ fmt_list = all_bayer_rggb;
+ break;
+ case PIX_FMT_ALL_GBRG:
+ fmt_list = all_bayer_gbrg;
+ break;
+ case PIX_FMT_ALL_GRBG:
+ fmt_list = all_bayer_grbg;
+ break;
+ }
+ if (fmt_list) {
+ for (k = 0; fmt_list[k].fourcc; k++) {
+ char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+ *active_fmt = *fmt;
+ active_fmt->fourcc = fmt_list[k].fourcc;
+ active_fmt->depth = fmt_list[k].depth;
+ unicam_dbg(2, unicam,
+ "matched fourcc: %s: code: %04x idx: %d\n",
+ v4l2_fourcc2s(fmt->fourcc,
+ fourcc_str),
+ fmt->code,
+ unicam->num_active_fmt);
+ unicam->num_active_fmt++;
+ active_fmt++;
+ }
+ } else {
+ char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+ *active_fmt = *fmt;
+ unicam_dbg(2, unicam,
+ "matched fourcc: %s: code: %04x idx: %d\n",
+ v4l2_fourcc2s(fmt->fourcc,
+ fourcc_str),
+ fmt->code,
+ unicam->num_active_fmt);
+ unicam->num_active_fmt++;
+ active_fmt++;
+ }
+ }
+ }
+ unicam_dbg(2, unicam,
+ "Done all formats\n");
+ dump_active_formats(unicam);
+
+ return 0;
+}
+
+static int unicam_probe_complete(struct unicam_device *unicam)
+{
+ struct video_device *vdev;
+ struct vb2_queue *q;
+ struct v4l2_mbus_framefmt mbus_fmt = {0};
+ const struct unicam_fmt *fmt;
+ int ret;
+
+ v4l2_set_subdev_hostdata(unicam->sensor, unicam);
+
+ unicam->v4l2_dev.notify = unicam_notify;
+
+ unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor);
+ if (!unicam->sensor_config)
+ return -ENOMEM;
+
+ ret = __subdev_get_format(unicam, &mbus_fmt);
+ if (ret) {
+ unicam_err(unicam, "Failed to get_format - ret %d\n", ret);
+ return ret;
+ }
+
+ fmt = find_format_by_code(unicam, mbus_fmt.code);
+ if (!fmt) {
+ unicam_dbg(3, unicam, "mbus code format (0x%08x) not found.\n",
+ mbus_fmt.code);
+ return -EINVAL;
+ }
+ unicam->fmt = fmt;
+ unicam->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+
+ /* Read current subdev format */
+ unicam_reset_format(unicam);
+
+ if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+ v4l2_std_id tvnorms;
+
+ if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video,
+ g_tvnorms)))
+ /*
+ * Subdevice should not advertise querystd but not
+ * g_tvnorms
+ */
+ return -EINVAL;
+
+ ret = v4l2_subdev_call(unicam->sensor, video,
+ g_tvnorms, &tvnorms);
+ if (WARN_ON(ret))
+ return -EINVAL;
+ unicam->video_dev.tvnorms |= tvnorms;
+ }
+
+ spin_lock_init(&unicam->dma_queue_lock);
+ mutex_init(&unicam->lock);
+
+ /* Add controls from the subdevice */
+ ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler,
+ unicam->sensor->ctrl_handler, NULL);
+ if (ret < 0)
+ return ret;
+
+ q = &unicam->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ q->drv_priv = unicam;
+ q->ops = &unicam_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct unicam_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &unicam->lock;
+ q->min_buffers_needed = 2;
+ q->dev = &unicam->pdev->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ unicam_err(unicam, "vb2_queue_init() failed\n");
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&unicam->dma_queue.active);
+
+ vdev = &unicam->video_dev;
+ strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &unicam_fops;
+ vdev->ioctl_ops = &unicam_ioctl_ops;
+ vdev->v4l2_dev = &unicam->v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = q;
+ vdev->lock = &unicam->lock;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+
+ video_set_drvdata(vdev, unicam);
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ unicam_err(unicam,
+ "Unable to register video device.\n");
+ return ret;
+ }
+
+ if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_STD);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_STD);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUMSTD);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERYSTD);
+ }
+ if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_EDID);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_EDID);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_DV_TIMINGS_CAP);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_DV_TIMINGS);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_DV_TIMINGS);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_DV_TIMINGS);
+ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERY_DV_TIMINGS);
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev);
+ if (ret) {
+ unicam_err(unicam,
+ "Unable to register subdev nodes.\n");
+ video_unregister_device(&unicam->video_dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int unicam_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct unicam_device *unicam = container_of(notifier->v4l2_dev,
+ struct unicam_device, v4l2_dev);
+
+ return unicam_probe_complete(unicam);
+}
+
+static int of_unicam_connect_subdevs(struct unicam_device *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct device_node *parent, *ep_node = NULL, *remote_ep = NULL,
+ *sensor_node = NULL;
+ struct v4l2_fwnode_endpoint *ep;
+ struct v4l2_async_subdev *asd;
+ int ret = -EINVAL;
+ unsigned int lane;
+ struct v4l2_async_subdev **subdevs = NULL;
+ unsigned int peripheral_data_lanes;
+
+ parent = pdev->dev.of_node;
+
+ asd = &dev->asd;
+ ep = &dev->endpoint;
+
+ ep_node = of_graph_get_next_endpoint(parent, NULL);
+ if (!ep_node) {
+ unicam_dbg(3, dev, "can't get next endpoint\n");
+ goto cleanup_exit;
+ }
+
+ unicam_dbg(3, dev, "ep_node is %s\n",
+ ep_node->name);
+
+ v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), ep);
+
+ for (lane = 0; lane < ep->bus.mipi_csi2.num_data_lanes; lane++) {
+ if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
+ unicam_err(dev, "Local endpoint - data lane reordering not supported\n");
+ goto cleanup_exit;
+ }
+ }
+
+ peripheral_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
+
+ sensor_node = of_graph_get_remote_port_parent(ep_node);
+ if (!sensor_node) {
+ unicam_dbg(3, dev, "can't get remote parent\n");
+ goto cleanup_exit;
+ }
+ unicam_dbg(3, dev, "sensor_node is %s\n",
+ sensor_node->name);
+ asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
+ asd->match.fwnode.fwnode = of_fwnode_handle(sensor_node);
+
+ remote_ep = of_graph_get_remote_endpoint(ep_node);
+ if (!remote_ep) {
+ unicam_dbg(3, dev, "can't get remote-endpoint\n");
+ goto cleanup_exit;
+ }
+ unicam_dbg(3, dev, "remote_ep is %s\n",
+ remote_ep->name);
+ v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), ep);
+ unicam_dbg(3, dev, "parsed remote_ep to endpoint. nr_of_link_frequencies %u, bus_type %u\n",
+ ep->nr_of_link_frequencies, ep->bus_type);
+
+ switch (ep->bus_type) {
+ case V4L2_MBUS_CSI2:
+ if (ep->bus.mipi_csi2.num_data_lanes >
+ peripheral_data_lanes) {
+ unicam_err(dev, "Subdevice %s wants too many data lanes (%u > %u)\n",
+ sensor_node->name,
+ ep->bus.mipi_csi2.num_data_lanes,
+ peripheral_data_lanes);
+ goto cleanup_exit;
+ }
+ for (lane = 0;
+ lane < ep->bus.mipi_csi2.num_data_lanes;
+ lane++) {
+ if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
+ unicam_err(dev, "Subdevice %s - incompatible data lane config\n",
+ sensor_node->name);
+ goto cleanup_exit;
+ }
+ }
+ dev->max_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
+ dev->bus_flags = ep->bus.mipi_csi2.flags;
+ break;
+ case V4L2_MBUS_CCP2:
+ if (ep->bus.mipi_csi1.clock_lane != 0 ||
+ ep->bus.mipi_csi1.data_lane != 1) {
+ unicam_err(dev, "Subdevice %s incompatible lane config\n",
+ sensor_node->name);
+ goto cleanup_exit;
+ }
+ dev->max_data_lanes = 1;
+ dev->bus_flags = ep->bus.mipi_csi1.strobe;
+ break;
+ default:
+ /* Unsupported bus type */
+ unicam_err(dev, "sub-device %s is not a CSI2 or CCP2 device %d\n",
+ sensor_node->name, ep->bus_type);
+ goto cleanup_exit;
+ }
+
+ /* Store bus type - CSI2 or CCP2 */
+ dev->bus_type = ep->bus_type;
+ unicam_dbg(3, dev, "bus_type is %d\n", dev->bus_type);
+
+ /* Store Virtual Channel number */
+ dev->virtual_channel = ep->base.id;
+
+ unicam_dbg(3, dev, "v4l2-endpoint: %s\n",
+ dev->bus_type == V4L2_MBUS_CSI2 ? "CSI2" : "CCP2");
+ unicam_dbg(3, dev, "Virtual Channel=%d\n", dev->virtual_channel);
+ if (dev->bus_type == V4L2_MBUS_CSI2)
+ unicam_dbg(3, dev, "flags=0x%08x\n",
+ ep->bus.mipi_csi2.flags);
+ unicam_dbg(3, dev, "num_data_lanes=%d\n", dev->max_data_lanes);
+
+ unicam_dbg(1, dev, "found sub-device %s\n",
+ sensor_node->name);
+
+ subdevs = devm_kzalloc(&dev->pdev->dev, sizeof(*subdevs), GFP_KERNEL);
+ if (!subdevs) {
+ ret = -ENOMEM;
+ goto cleanup_exit;
+ }
+ subdevs[0] = asd;
+ dev->notifier.subdevs = subdevs;
+ dev->notifier.num_subdevs = 1;
+ dev->notifier.bound = unicam_async_bound;
+ dev->notifier.complete = unicam_async_complete;
+ ret = v4l2_async_notifier_register(&dev->v4l2_dev,
+ &dev->notifier);
+ if (ret) {
+ unicam_err(dev, "Error registering async notifier - ret %d\n",
+ ret);
+ ret = -EINVAL;
+ }
+
+cleanup_exit:
+ if (remote_ep)
+ of_node_put(remote_ep);
+ if (sensor_node)
+ of_node_put(sensor_node);
+ if (ep_node)
+ of_node_put(ep_node);
+
+ return ret;
+}
+
+static int unicam_probe(struct platform_device *pdev)
+{
+ struct unicam_cfg *unicam_cfg;
+ struct unicam_device *unicam;
+ struct v4l2_ctrl_handler *hdl;
+ struct resource *res;
+ int ret;
+
+ unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
+ if (!unicam)
+ return -ENOMEM;
+
+ unicam->pdev = pdev;
+ unicam_cfg = &unicam->cfg;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(unicam_cfg->base)) {
+ unicam_err(unicam, "Failed to get main io block\n");
+ return PTR_ERR(unicam_cfg->base);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(unicam_cfg->clk_gate_base)) {
+ unicam_err(unicam, "Failed to get 2nd io block\n");
+ return PTR_ERR(unicam_cfg->clk_gate_base);
+ }
+
+ unicam->clock = devm_clk_get(&pdev->dev, "lp");
+ 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;
+ }
+
+ ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0,
+ "unicam_capture0", unicam);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request interrupt\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
+ if (ret) {
+ unicam_err(unicam,
+ "Unable to register v4l2 device.\n");
+ return ret;
+ }
+
+ /* Reserve space for the controls */
+ hdl = &unicam->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret < 0)
+ goto probe_out_v4l2_unregister;
+ unicam->v4l2_dev.ctrl_handler = hdl;
+
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, unicam);
+
+ ret = of_unicam_connect_subdevs(unicam);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to connect subdevs\n");
+ goto free_hdl;
+ }
+
+ /* Enable the block power domain */
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+free_hdl:
+ v4l2_ctrl_handler_free(hdl);
+probe_out_v4l2_unregister:
+ v4l2_device_unregister(&unicam->v4l2_dev);
+ return ret;
+}
+
+static int unicam_remove(struct platform_device *pdev)
+{
+ struct unicam_device *unicam = platform_get_drvdata(pdev);
+
+ unicam_dbg(2, unicam, "%s\n", __func__);
+
+ pm_runtime_disable(&pdev->dev);
+
+ v4l2_async_notifier_unregister(&unicam->notifier);
+ v4l2_ctrl_handler_free(&unicam->ctrl_handler);
+ v4l2_device_unregister(&unicam->v4l2_dev);
+ video_unregister_device(&unicam->video_dev);
+ if (unicam->sensor_config)
+ v4l2_subdev_free_pad_config(unicam->sensor_config);
+
+ return 0;
+}
+
+static const struct of_device_id unicam_of_match[] = {
+ { .compatible = "brcm,bcm2835-unicam", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, unicam_of_match);
+
+static struct platform_driver unicam_driver = {
+ .probe = unicam_probe,
+ .remove = unicam_remove,
+ .driver = {
+ .name = UNICAM_MODULE_NAME,
+ .of_match_table = of_match_ptr(unicam_of_match),
+ },
+};
+
+module_platform_driver(unicam_driver);
+
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.org>");
+MODULE_DESCRIPTION("BCM2835 Unicam driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(UNICAM_VERSION);
diff --git a/drivers/media/platform/bcm2835/vc4-regs-unicam.h b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
new file mode 100644
index 0000000..28e9a75
--- /dev/null
+++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 Raspberry Pi Trading.
+ * Dave Stevenson <dave.stevenson@raspberrypi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef VC4_REGS_UNICAM_H
+#define VC4_REGS_UNICAM_H
+
+/*
+ * The following values are taken from files found within the code drop
+ * made by Broadcom for the BCM21553 Graphics Driver, predominantly in
+ * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h.
+ * They have been modified to be only the register offset.
+ */
+#define UNICAM_CTRL 0x000
+#define UNICAM_STA 0x004
+#define UNICAM_ANA 0x008
+#define UNICAM_PRI 0x00c
+#define UNICAM_CLK 0x010
+#define UNICAM_CLT 0x014
+#define UNICAM_DAT0 0x018
+#define UNICAM_DAT1 0x01c
+#define UNICAM_DAT2 0x020
+#define UNICAM_DAT3 0x024
+#define UNICAM_DLT 0x028
+#define UNICAM_CMP0 0x02c
+#define UNICAM_CMP1 0x030
+#define UNICAM_CAP0 0x034
+#define UNICAM_CAP1 0x038
+#define UNICAM_ICTL 0x100
+#define UNICAM_ISTA 0x104
+#define UNICAM_IDI0 0x108
+#define UNICAM_IPIPE 0x10c
+#define UNICAM_IBSA0 0x110
+#define UNICAM_IBEA0 0x114
+#define UNICAM_IBLS 0x118
+#define UNICAM_IBWP 0x11c
+#define UNICAM_IHWIN 0x120
+#define UNICAM_IHSTA 0x124
+#define UNICAM_IVWIN 0x128
+#define UNICAM_IVSTA 0x12c
+#define UNICAM_ICC 0x130
+#define UNICAM_ICS 0x134
+#define UNICAM_IDC 0x138
+#define UNICAM_IDPO 0x13c
+#define UNICAM_IDCA 0x140
+#define UNICAM_IDCD 0x144
+#define UNICAM_IDS 0x148
+#define UNICAM_DCS 0x200
+#define UNICAM_DBSA0 0x204
+#define UNICAM_DBEA0 0x208
+#define UNICAM_DBWP 0x20c
+#define UNICAM_DBCTL 0x300
+#define UNICAM_IBSA1 0x304
+#define UNICAM_IBEA1 0x308
+#define UNICAM_IDI1 0x30c
+#define UNICAM_DBSA1 0x310
+#define UNICAM_DBEA1 0x314
+#define UNICAM_MISC 0x400
+
+/*
+ * The following bitmasks are from the kernel released by Broadcom
+ * for Android - https://android.googlesource.com/kernel/bcm/
+ * The Rhea, Hawaii, and Java chips all contain the same VideoCore4
+ * Unicam block as BCM2835, as defined in eg
+ * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar.
+ * Values reworked to use the kernel BIT and GENMASK macros.
+ *
+ * Some of the bit mnenomics have been amended to match the datasheet.
+ */
+/* UNICAM_CTRL Register */
+#define UNICAM_CPE BIT(0)
+#define UNICAM_MEM BIT(1)
+#define UNICAM_CPR BIT(2)
+#define UNICAM_CPM_MASK GENMASK(3, 3)
+#define UNICAM_CPM_CSI2 0
+#define UNICAM_CPM_CCP2 1
+#define UNICAM_SOE BIT(4)
+#define UNICAM_DCM_MASK GENMASK(5, 5)
+#define UNICAM_DCM_STROBE 0
+#define UNICAM_DCM_DATA 1
+#define UNICAM_SLS BIT(6)
+#define UNICAM_PFT_MASK GENMASK(11, 8)
+#define UNICAM_OET_MASK GENMASK(20, 12)
+
+/* UNICAM_STA Register */
+#define UNICAM_SYN BIT(0)
+#define UNICAM_CS BIT(1)
+#define UNICAM_SBE BIT(2)
+#define UNICAM_PBE BIT(3)
+#define UNICAM_HOE BIT(4)
+#define UNICAM_PLE BIT(5)
+#define UNICAM_SSC BIT(6)
+#define UNICAM_CRCE BIT(7)
+#define UNICAM_OES BIT(8)
+#define UNICAM_IFO BIT(9)
+#define UNICAM_OFO BIT(10)
+#define UNICAM_BFO BIT(11)
+#define UNICAM_DL BIT(12)
+#define UNICAM_PS BIT(13)
+#define UNICAM_IS BIT(14)
+#define UNICAM_PI0 BIT(15)
+#define UNICAM_PI1 BIT(16)
+#define UNICAM_FSI_S BIT(17)
+#define UNICAM_FEI_S BIT(18)
+#define UNICAM_LCI_S BIT(19)
+#define UNICAM_BUF0_RDY BIT(20)
+#define UNICAM_BUF0_NO BIT(21)
+#define UNICAM_BUF1_RDY BIT(22)
+#define UNICAM_BUF1_NO BIT(23)
+#define UNICAM_DI BIT(24)
+
+#define UNICAM_STA_MASK_ALL \
+ (UNICAM_DL + \
+ UNICAM_SBE + \
+ UNICAM_PBE + \
+ UNICAM_HOE + \
+ UNICAM_PLE + \
+ UNICAM_SSC + \
+ UNICAM_CRCE + \
+ UNICAM_IFO + \
+ UNICAM_OFO + \
+ UNICAM_PS + \
+ UNICAM_PI0 + \
+ UNICAM_PI1)
+
+/* UNICAM_ANA Register */
+#define UNICAM_APD BIT(0)
+#define UNICAM_BPD BIT(1)
+#define UNICAM_AR BIT(2)
+#define UNICAM_DDL BIT(3)
+#define UNICAM_CTATADJ_MASK GENMASK(7, 4)
+#define UNICAM_PTATADJ_MASK GENMASK(11, 8)
+
+/* UNICAM_PRI Register */
+#define UNICAM_PE BIT(0)
+#define UNICAM_PT_MASK GENMASK(2, 1)
+#define UNICAM_NP_MASK GENMASK(7, 4)
+#define UNICAM_PP_MASK GENMASK(11, 8)
+#define UNICAM_BS_MASK GENMASK(15, 12)
+#define UNICAM_BL_MASK GENMASK(17, 16)
+
+/* UNICAM_CLK Register */
+#define UNICAM_CLE BIT(0)
+#define UNICAM_CLPD BIT(1)
+#define UNICAM_CLLPE BIT(2)
+#define UNICAM_CLHSE BIT(3)
+#define UNICAM_CLTRE BIT(4)
+#define UNICAM_CLAC_MASK GENMASK(8, 5)
+#define UNICAM_CLSTE BIT(29)
+
+/* UNICAM_CLT Register */
+#define UNICAM_CLT1_MASK GENMASK(7, 0)
+#define UNICAM_CLT2_MASK GENMASK(15, 8)
+
+/* UNICAM_DATn Registers */
+#define UNICAM_DLE BIT(0)
+#define UNICAM_DLPD BIT(1)
+#define UNICAM_DLLPE BIT(2)
+#define UNICAM_DLHSE BIT(3)
+#define UNICAM_DLTRE BIT(4)
+#define UNICAM_DLSM BIT(5)
+#define UNICAM_DLFO BIT(28)
+#define UNICAM_DLSTE BIT(29)
+
+#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO)
+
+/* UNICAM_DLT Register */
+#define UNICAM_DLT1_MASK GENMASK(7, 0)
+#define UNICAM_DLT2_MASK GENMASK(15, 8)
+#define UNICAM_DLT3_MASK GENMASK(23, 16)
+
+/* UNICAM_ICTL Register */
+#define UNICAM_FSIE BIT(0)
+#define UNICAM_FEIE BIT(1)
+#define UNICAM_IBOB BIT(2)
+#define UNICAM_FCM BIT(3)
+#define UNICAM_TFC BIT(4)
+#define UNICAM_LIP_MASK GENMASK(6, 5)
+#define UNICAM_LCIE_MASK GENMASK(28, 16)
+
+/* UNICAM_IDI0/1 Register */
+#define UNICAM_ID0_MASK GENMASK(7, 0)
+#define UNICAM_ID1_MASK GENMASK(15, 8)
+#define UNICAM_ID2_MASK GENMASK(23, 16)
+#define UNICAM_ID3_MASK GENMASK(31, 24)
+
+/* UNICAM_ISTA Register */
+#define UNICAM_FSI BIT(0)
+#define UNICAM_FEI BIT(1)
+#define UNICAM_LCI BIT(2)
+
+#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI)
+
+/* UNICAM_IPIPE Register */
+#define UNICAM_PUM_MASK GENMASK(2, 0)
+ /* Unpacking modes */
+ #define UNICAM_PUM_NONE 0
+ #define UNICAM_PUM_UNPACK6 1
+ #define UNICAM_PUM_UNPACK7 2
+ #define UNICAM_PUM_UNPACK8 3
+ #define UNICAM_PUM_UNPACK10 4
+ #define UNICAM_PUM_UNPACK12 5
+ #define UNICAM_PUM_UNPACK14 6
+ #define UNICAM_PUM_UNPACK16 7
+#define UNICAM_DDM_MASK GENMASK(6, 3)
+#define UNICAM_PPM_MASK GENMASK(9, 7)
+ /* Packing modes */
+ #define UNICAM_PPM_NONE 0
+ #define UNICAM_PPM_PACK8 1
+ #define UNICAM_PPM_PACK10 2
+ #define UNICAM_PPM_PACK12 3
+ #define UNICAM_PPM_PACK14 4
+ #define UNICAM_PPM_PACK16 5
+#define UNICAM_DEM_MASK GENMASK(11, 10)
+#define UNICAM_DEBL_MASK GENMASK(14, 12)
+#define UNICAM_ICM_MASK GENMASK(16, 15)
+#define UNICAM_IDM_MASK GENMASK(17, 17)
+
+/* UNICAM_ICC Register */
+#define UNICAM_ICFL_MASK GENMASK(4, 0)
+#define UNICAM_ICFH_MASK GENMASK(9, 5)
+#define UNICAM_ICST_MASK GENMASK(12, 10)
+#define UNICAM_ICLT_MASK GENMASK(15, 13)
+#define UNICAM_ICLL_MASK GENMASK(31, 16)
+
+/* UNICAM_DCS Register */
+#define UNICAM_DIE BIT(0)
+#define UNICAM_DIM BIT(1)
+#define UNICAM_DBOB BIT(3)
+#define UNICAM_FDE BIT(4)
+#define UNICAM_LDP BIT(5)
+#define UNICAM_EDL_MASK GENMASK(15, 8)
+
+/* UNICAM_DBCTL Register */
+#define UNICAM_DBEN BIT(0)
+#define UNICAM_BUF0_IE BIT(1)
+#define UNICAM_BUF1_IE BIT(2)
+
+/* UNICAM_CMP[0,1] register */
+#define UNICAM_PCE BIT(31)
+#define UNICAM_GI BIT(9)
+#define UNICAM_CPH BIT(8)
+#define UNICAM_PCVC_MASK GENMASK(7, 6)
+#define UNICAM_PCDT_MASK GENMASK(5, 0)
+
+/* UNICAM_MISC register */
+#define UNICAM_FL0 BIT(6)
+#define UNICAM_FL1 BIT(9)
+
+#endif
--
2.7.4
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v3 4/4] MAINTAINERS: Add entry for BCM2835 camera driver
2017-09-20 16:07 [PATCH v3 0/4] BCM283x Camera Receiver driver Dave Stevenson
` (2 preceding siblings ...)
2017-09-20 16:07 ` [PATCH v3 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
@ 2017-09-20 16:07 ` Dave Stevenson
3 siblings, 0 replies; 30+ messages in thread
From: Dave Stevenson @ 2017-09-20 16:07 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
devicetree
Cc: Dave Stevenson
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
No changes from v2 to v3
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index eb930eb..b47ddaa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2713,6 +2713,13 @@ S: Maintained
N: bcm2835
F: drivers/staging/vc04_services
+BROADCOM BCM2835 CAMERA DRIVER
+M: Dave Stevenson <dave.stevenson@raspberrypi.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/platform/bcm2835/
+F: Documentation/devicetree/bindings/media/bcm2835-unicam.txt
+
BROADCOM BCM47XX MIPS ARCHITECTURE
M: Hauke Mehrtens <hauke@hauke-m.de>
M: Rafał Miłecki <zajec5@gmail.com>
--
2.7.4
^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-09-20 16:07 ` [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
@ 2017-09-22 6:45 ` Stefan Wahren
0 siblings, 0 replies; 30+ messages in thread
From: Stefan Wahren @ 2017-09-22 6:45 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Eric Anholt,
linux-media-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Dave Stevenson,
Sakari Ailus, Hans Verkuil,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
Hi Dave,
> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> hat am 20. September 2017 um 18:07 geschrieben:
>
>
> Document the DT bindings for the CSI2/CCP2 receiver peripheral
> (known as Unicam) on BCM283x SoCs.
>
> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
> ---
>
> Changes since v2
> - Removed all references to Linux drivers.
> - Reworded section about disabling the firmware driver.
> - Renamed clock from "lp_clock" to "lp" in description and example.
> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
> and data-lanes.
> - Corrected typo in example from csi to csi1.
> - Removed unnecessary #address-cells and #size-cells in example.
> - Removed setting of status from the example.
>
> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
> 1 file changed, 85 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..7714fb3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> @@ -0,0 +1,85 @@
> +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.
> +
> +The main platform using this SoC is the Raspberry Pi family of boards.
> +On the Pi the VideoCore firmware can also control this hardware block,
> +and driving it from two different processors will cause issues.
> +To avoid this, the firmware checks the device tree configuration
> +during boot. If it finds device tree nodes called csi0 or csi1 then
> +it will stop the firmware accessing the block, and it can then
> +safely be used via the device tree binding.
> +
> +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" entry, matching entries in the
> + clocks property.
> +
> +Unicam supports a single port node. It should contain one 'port' child node
> +with child 'endpoint' node. Please refer to the bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
> +are mandatory.
> +Data lane reordering is not supported so the data lanes must be in order,
> +starting at 1. The number of data lanes should represent the number of
> +usable lanes for the hardware block. That may be limited by either the SoC or
> +how the platform presents the interface, and the lower value must be used.
> +
> +Lane reordering is not supported on the clock lane either, so the optional
> +property "clock-lane" will implicitly be <0>.
> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> +implicitly be <0 0 0 0 0>.
> +Neither of these values will be checked.
> +
> +Example:
> + csi1: csi1@7e801000 {
> + compatible = "brcm,bcm2835-unicam";
> + reg = <0x7e801000 0x800>,
> + <0x7e802004 0x4>;
sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
Regards
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-09-22 6:45 ` Stefan Wahren
0 siblings, 0 replies; 30+ messages in thread
From: Stefan Wahren @ 2017-09-22 6:45 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Eric Anholt, linux-media, Rob Herring,
Dave Stevenson, Sakari Ailus, Hans Verkuil, linux-rpi-kernel,
devicetree
Hi Dave,
> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>
>
> 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>
> ---
>
> Changes since v2
> - Removed all references to Linux drivers.
> - Reworded section about disabling the firmware driver.
> - Renamed clock from "lp_clock" to "lp" in description and example.
> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
> and data-lanes.
> - Corrected typo in example from csi to csi1.
> - Removed unnecessary #address-cells and #size-cells in example.
> - Removed setting of status from the example.
>
> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
> 1 file changed, 85 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..7714fb3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> @@ -0,0 +1,85 @@
> +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.
> +
> +The main platform using this SoC is the Raspberry Pi family of boards.
> +On the Pi the VideoCore firmware can also control this hardware block,
> +and driving it from two different processors will cause issues.
> +To avoid this, the firmware checks the device tree configuration
> +during boot. If it finds device tree nodes called csi0 or csi1 then
> +it will stop the firmware accessing the block, and it can then
> +safely be used via the device tree binding.
> +
> +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" entry, matching entries in the
> + clocks property.
> +
> +Unicam supports a single port node. It should contain one 'port' child node
> +with child 'endpoint' node. Please refer to the bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
> +are mandatory.
> +Data lane reordering is not supported so the data lanes must be in order,
> +starting at 1. The number of data lanes should represent the number of
> +usable lanes for the hardware block. That may be limited by either the SoC or
> +how the platform presents the interface, and the lower value must be used.
> +
> +Lane reordering is not supported on the clock lane either, so the optional
> +property "clock-lane" will implicitly be <0>.
> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> +implicitly be <0 0 0 0 0>.
> +Neither of these values will be checked.
> +
> +Example:
> + csi1: csi1@7e801000 {
> + compatible = "brcm,bcm2835-unicam";
> + reg = <0x7e801000 0x800>,
> + <0x7e802004 0x4>;
sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
Regards
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
2017-09-20 16:07 ` [PATCH v3 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
@ 2017-09-22 10:37 ` Hans Verkuil
0 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2017-09-22 10:37 UTC (permalink / raw)
To: Dave Stevenson, Mauro Carvalho Chehab, Rob Herring, Hans Verkuil,
Eric Anholt, Stefan Wahren, Sakari Ailus, linux-media,
linux-rpi-kernel, devicetree
Hi Dave,
Thank you for working on this!
See my review below:
On 20/09/17 18:07, Dave Stevenson wrote:
> Add driver for the Unicam camera receiver block on
> BCM283x processors.
>
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
> ---
>
> Changes from v2.
> - Don't store the irq value as it isn't used outside the probe.
> - Change PIX_FMT_ALL_ defines to avoid potential clashes with 4CCs.
> - Clock now called "lp" and not "lp_clock".
> - Minor description changes.
>
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/bcm2835/Kconfig | 14 +
> drivers/media/platform/bcm2835/Makefile | 3 +
> drivers/media/platform/bcm2835/bcm2835-unicam.c | 2192 ++++++++++++++++++++++
> drivers/media/platform/bcm2835/vc4-regs-unicam.h | 264 +++
> 6 files changed, 2475 insertions(+)
> create mode 100644 drivers/media/platform/bcm2835/Kconfig
> create mode 100644 drivers/media/platform/bcm2835/Makefile
> create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c
> create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 7e7cc49..1e5f004 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -150,6 +150,7 @@ source "drivers/media/platform/am437x/Kconfig"
> source "drivers/media/platform/xilinx/Kconfig"
> source "drivers/media/platform/rcar-vin/Kconfig"
> source "drivers/media/platform/atmel/Kconfig"
> +source "drivers/media/platform/bcm2835/Kconfig"
>
> config VIDEO_TI_CAL
> tristate "TI CAL (Camera Adaptation Layer) driver"
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index c1ef946..045de3f 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -90,3 +90,4 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/
> obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
>
> obj-y += meson/
> +obj-y += bcm2835/
> diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig
> new file mode 100644
> index 0000000..6a74842
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/Kconfig
> @@ -0,0 +1,14 @@
> +# Broadcom VideoCore4 V4L2 camera support
> +
> +config VIDEO_BCM2835_UNICAM
> + tristate "Broadcom BCM2835 Unicam video capture driver"
> + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> + depends on ARCH_BCM2835 || COMPILE_TEST
> + select VIDEOBUF2_DMA_CONTIG
> + select V4L2_FWNODE
> + ---help---
> + Say Y here to enable V4L2 subdevice for CSI2 receiver.
> + This is a V4L2 subdevice that interfaces directly to the VC4 peripheral.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called bcm2835-unicam.
> diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile
> new file mode 100644
> index 0000000..a98aba0
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/Makefile
> @@ -0,0 +1,3 @@
> +# Makefile for BCM2835 Unicam driver
> +
> +obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o
> diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
> new file mode 100644
> index 0000000..93831fb
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
> @@ -0,0 +1,2192 @@
> +/*
> + * BCM2835 Unicam capture Driver
> + *
> + * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
> + *
> + * Dave Stevenson <dave.stevenson@raspberrypi.org>
> + *
> + * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
> + * TI CAL camera interface driver by Benoit Parrot.
> + *
> + *
> + * There are two camera drivers in the kernel for BCM283x - this one
> + * and bcm2835-camera (currently in staging).
> + *
> + * This driver directly controls the Unicam peripheral - there is no
> + * involvement with the VideoCore firmware. Unicam receives CSI-2 or
> + * CCP2 data and writes it into SDRAM. The only processing options are
> + * to repack Bayer data into an alternate format, and applying windowing
> + * (currently not implemented).
> + * It should be possible to connect it to any sensor with a
> + * suitable output interface and V4L2 subdevice driver.
> + *
> + * bcm2835-camera uses the VideoCore firmware to control the sensor,
> + * Unicam, ISP, and all tuner control loops. Fully processed frames are
> + * delivered to the driver by the firmware. It only has sensor drivers
> + * for Omnivision OV5647, and Sony IMX219 sensors.
> + *
> + * The two drivers are mutually exclusive for the same Unicam instance.
> + * The VideoCore firmware checks the device tree configuration during boot.
> + * If it finds device tree nodes called csi0 or csi1 it will block the
> + * firmware from accessing the peripheral, and bcm2835-camera will
> + * not be able to stream data.
> + *
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-dv-timings.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/videobuf2-v4l2.h>
You can drop this include, videobuf2-dma-contig.h pulls it in for you.
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "vc4-regs-unicam.h"
> +
> +#define UNICAM_MODULE_NAME "unicam"
> +#define UNICAM_VERSION "0.1.0"
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "Debug level 0-3");
> +
> +#define unicam_dbg(level, dev, fmt, arg...) \
> + v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg)
> +#define unicam_info(dev, fmt, arg...) \
> + v4l2_info(&(dev)->v4l2_dev, fmt, ##arg)
> +#define unicam_err(dev, fmt, arg...) \
> + v4l2_err(&(dev)->v4l2_dev, fmt, ##arg)
> +
> +/*
> + * Stride is a 16 bit register, but also has to be a multiple of 16.
> + */
> +#define BPL_ALIGNMENT 16
> +#define MAX_BYTESPERLINE ((1 << 16) - BPL_ALIGNMENT)
> +/*
> + * Max width is therefore determined by the max stride divided by
> + * the number of bits per pixel. Take 32bpp as a
> + * worst case.
> + * No imposed limit on the height, so adopt a square image for want
> + * of anything better.
> + */
> +#define MAX_WIDTH (MAX_BYTESPERLINE / 4)
> +#define MAX_HEIGHT MAX_WIDTH
> +/* Define a nominal minimum image size */
> +#define MIN_WIDTH 16
> +#define MIN_HEIGHT 16
> +/*
> + * Whilst Unicam doesn't require any additional padding on the image
> + * height, various other parts of the BCM283x frameworks require a multiple
> + * of 16.
> + * Seeing as image buffers are significantly larger than this extra
> + * padding, add it in order to simplify integration.
> + */
> +#define HEIGHT_ALIGNMENT 16
> +
> +/*
> + * struct unicam_fmt - Unicam media bus format information
> + * @pixelformat: V4L2 pixel format FCC identifier.
> + * @code: V4L2 media bus format code.
> + * @depth: Bits per pixel (when stored in memory).
> + * @csi_dt: CSI data type.
> + */
> +struct unicam_fmt {
> + u32 fourcc;
> + u32 code;
> + u8 depth;
> + u8 csi_dt;
> +};
> +
> +/*
> + * The peripheral can unpack and repack between several of
> + * the Bayer raw formats, so any Bayer format can be advertised
> + * as the same Bayer order in each of the supported bit depths.
> + * These values are used as flags to denote that all Bayer formats
> + * in the specified order should be advertised. They are not
> + * used outside of this driver but must avoid collisions with
> + * the standard V4L2_PIX_FMT_ values, hence 0xFFFFFFxx.
> + */
> +#define PIX_FMT_ALL_BGGR 0xFFFFFFF0
> +#define PIX_FMT_ALL_RGGB 0xFFFFFFF1
> +#define PIX_FMT_ALL_GBRG 0xFFFFFFF2
> +#define PIX_FMT_ALL_GRBG 0xFFFFFFF3
Use lower case 'f' (linux coding style)
> +
> +static const struct unicam_fmt formats[] = {
> + /* YUV Formats */
> + {
> + .fourcc = V4L2_PIX_FMT_YUYV,
> + .code = MEDIA_BUS_FMT_YUYV8_2X8,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + .fourcc = V4L2_PIX_FMT_UYVY,
> + .code = MEDIA_BUS_FMT_UYVY8_2X8,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + .fourcc = V4L2_PIX_FMT_YVYU,
> + .code = MEDIA_BUS_FMT_YVYU8_2X8,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + .fourcc = V4L2_PIX_FMT_VYUY,
> + .code = MEDIA_BUS_FMT_VYUY8_2X8,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + .fourcc = V4L2_PIX_FMT_YUYV,
> + .code = MEDIA_BUS_FMT_YUYV8_1X16,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + .fourcc = V4L2_PIX_FMT_UYVY,
> + .code = MEDIA_BUS_FMT_UYVY8_1X16,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + .fourcc = V4L2_PIX_FMT_YVYU,
> + .code = MEDIA_BUS_FMT_YVYU8_1X16,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + .fourcc = V4L2_PIX_FMT_VYUY,
> + .code = MEDIA_BUS_FMT_VYUY8_1X16,
> + .depth = 16,
> + .csi_dt = 0x1e,
> + }, {
> + /* RGB Formats */
> + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
> + .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
> + .depth = 16,
> + .csi_dt = 0x22,
> + }, {
> + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
> + .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
> + .depth = 16,
> + .csi_dt = 0x22
> + }, {
> + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
> + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
> + .depth = 16,
> + .csi_dt = 0x21,
> + }, {
> + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
> + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
> + .depth = 16,
> + .csi_dt = 0x21,
> + }, {
> + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
> + .code = MEDIA_BUS_FMT_RGB888_1X24,
> + .depth = 24,
> + .csi_dt = 0x24,
> + }, {
> + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
> + .code = MEDIA_BUS_FMT_BGR888_1X24,
> + .depth = 24,
> + .csi_dt = 0x24,
> + }, {
> + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
> + .code = MEDIA_BUS_FMT_ARGB8888_1X32,
> + .depth = 32,
> + .csi_dt = 0x0,
> + }, {
> + /* Bayer Formats */
> + .fourcc = PIX_FMT_ALL_BGGR,
> + .code = MEDIA_BUS_FMT_SBGGR8_1X8,
> + .depth = 8,
> + .csi_dt = 0x2a,
> + }, {
> + .fourcc = PIX_FMT_ALL_GBRG,
> + .code = MEDIA_BUS_FMT_SGBRG8_1X8,
> + .depth = 8,
> + .csi_dt = 0x2a,
> + }, {
> + .fourcc = PIX_FMT_ALL_GRBG,
> + .code = MEDIA_BUS_FMT_SGRBG8_1X8,
> + .depth = 8,
> + .csi_dt = 0x2a,
> + }, {
> + .fourcc = PIX_FMT_ALL_RGGB,
> + .code = MEDIA_BUS_FMT_SRGGB8_1X8,
> + .depth = 8,
> + .csi_dt = 0x2a,
> + }, {
> + .fourcc = PIX_FMT_ALL_BGGR,
> + .code = MEDIA_BUS_FMT_SBGGR10_1X10,
> + .depth = 10,
> + .csi_dt = 0x2b,
> + }, {
> + .fourcc = PIX_FMT_ALL_GBRG,
> + .code = MEDIA_BUS_FMT_SGBRG10_1X10,
> + .depth = 10,
> + .csi_dt = 0x2b,
> + }, {
> + .fourcc = PIX_FMT_ALL_GRBG,
> + .code = MEDIA_BUS_FMT_SGRBG10_1X10,
> + .depth = 10,
> + .csi_dt = 0x2b,
> + }, {
> + .fourcc = PIX_FMT_ALL_RGGB,
> + .code = MEDIA_BUS_FMT_SRGGB10_1X10,
> + .depth = 10,
> + .csi_dt = 0x2b,
> + }, {
> + .fourcc = PIX_FMT_ALL_BGGR,
> + .code = MEDIA_BUS_FMT_SBGGR12_1X12,
> + .depth = 12,
> + .csi_dt = 0x2c,
> + }, {
> + .fourcc = PIX_FMT_ALL_GBRG,
> + .code = MEDIA_BUS_FMT_SGBRG12_1X12,
> + .depth = 12,
> + .csi_dt = 0x2c,
> + }, {
> + .fourcc = PIX_FMT_ALL_GRBG,
> + .code = MEDIA_BUS_FMT_SGRBG12_1X12,
> + .depth = 12,
> + .csi_dt = 0x2c,
> + }, {
> + .fourcc = PIX_FMT_ALL_RGGB,
> + .code = MEDIA_BUS_FMT_SRGGB12_1X12,
> + .depth = 12,
> + .csi_dt = 0x2c,
> + }, {
> + .fourcc = PIX_FMT_ALL_BGGR,
> + .code = MEDIA_BUS_FMT_SBGGR14_1X14,
> + .depth = 14,
> + .csi_dt = 0x2d,
> + }, {
> + .fourcc = PIX_FMT_ALL_GBRG,
> + .code = MEDIA_BUS_FMT_SGBRG14_1X14,
> + .depth = 14,
> + .csi_dt = 0x2d,
> + }, {
> + .fourcc = PIX_FMT_ALL_GRBG,
> + .code = MEDIA_BUS_FMT_SGRBG14_1X14,
> + .depth = 14,
> + .csi_dt = 0x2d,
> + }, {
> + .fourcc = PIX_FMT_ALL_RGGB,
> + .code = MEDIA_BUS_FMT_SRGGB14_1X14,
> + .depth = 14,
> + .csi_dt = 0x2d,
> + },
> +};
> +
> +struct bayer_fmt {
> + u32 fourcc;
> + u8 depth;
> +};
> +
> +const struct bayer_fmt all_bayer_bggr[] = {
> + {V4L2_PIX_FMT_SBGGR8, 8},
> + {V4L2_PIX_FMT_SBGGR10P, 10},
> + {V4L2_PIX_FMT_SBGGR12, 12},
> + {V4L2_PIX_FMT_SBGGR16, 16},
> + {0, 0}
> +};
> +
> +const struct bayer_fmt all_bayer_rggb[] = {
> + {V4L2_PIX_FMT_SRGGB8, 8},
> + {V4L2_PIX_FMT_SRGGB10P, 10},
> + {V4L2_PIX_FMT_SRGGB12, 12},
> + {V4L2_PIX_FMT_SRGGB16, 16},
> + {0, 0}
> +};
> +
> +const struct bayer_fmt all_bayer_gbrg[] = {
> + {V4L2_PIX_FMT_SGBRG8, 8},
> + {V4L2_PIX_FMT_SGBRG10P, 10},
> + {V4L2_PIX_FMT_SGBRG12, 12},
> + {V4L2_PIX_FMT_SGBRG16, 16},
> + {0, 0}
> +};
> +
> +const struct bayer_fmt all_bayer_grbg[] = {
> + {V4L2_PIX_FMT_SGRBG8, 8},
> + {V4L2_PIX_FMT_SGRBG10P, 10},
> + {V4L2_PIX_FMT_SGRBG12, 12},
> + {V4L2_PIX_FMT_SGRBG16, 16},
> + {0, 0}
> +};
> +
> +struct unicam_dmaqueue {
> + struct list_head active;
> +};
> +
> +struct unicam_buffer {
> + struct vb2_v4l2_buffer vb;
> + struct list_head list;
> +};
> +
> +struct unicam_cfg {
> + /* peripheral base address */
> + void __iomem *base;
> + /* clock gating base address */
> + void __iomem *clk_gate_base;
> +};
> +
> +#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))
You're counting the bayer formats in the formats[] array as well, but
that's not really correct, is it? Those shouldn't be counted since the
all_bayer_* arrays take care of those formats. I'm not sure what the best
option is here. One is to split off the bayer formats into their own
bayer_formats array.
BTW, I assume we're talking MAX_POSSIBLE_PIX_FMTS (might be a better name).
> +
> +struct unicam_device {
> + /* V4l2 specific parameters */
> + /* Identifies video device for this channel */
> + struct video_device video_dev;
> + struct v4l2_ctrl_handler ctrl_handler;
> +
> + struct v4l2_fwnode_endpoint endpoint;
> +
> + struct v4l2_async_subdev asd;
> +
> + /* unicam cfg */
> + struct unicam_cfg cfg;
> + /* clock handle */
> + struct clk *clock;
> + /* V4l2 device */
> + struct v4l2_device v4l2_dev;
> + /* parent device */
> + struct platform_device *pdev;
> + /* subdevice async Notifier */
> + struct v4l2_async_notifier notifier;
> + unsigned int sequence;
> +
> + /* ptr to sub device */
> + struct v4l2_subdev *sensor;
> + /* Pad config for the sensor */
> + struct v4l2_subdev_pad_config *sensor_config;
> + /* current input at the sub device */
> + int current_input;
> +
> + /* Pointer pointing to current v4l2_buffer */
> + struct unicam_buffer *cur_frm;
> + /* Pointer pointing to next v4l2_buffer */
> + struct unicam_buffer *next_frm;
> +
> + /* video capture */
> + const struct unicam_fmt *fmt;
> + /* Used to store current pixel format */
> + struct v4l2_format v_fmt;
> + /* Used to store current mbus frame format */
> + struct v4l2_mbus_framefmt m_fmt;
> +
> + struct unicam_fmt active_fmts[MAX_POSSIBLE_FMTS];
> + int num_active_fmt;
> + unsigned int virtual_channel;
> + enum v4l2_mbus_type bus_type;
> + /*
> + * Stores bus.mipi_csi2.flags for CSI2 sensors, or
> + * bus.mipi_csi1.strobe for CCP2.
> + */
> + unsigned int bus_flags;
> + unsigned int max_data_lanes;
> + unsigned int active_data_lanes;
> +
> + struct v4l2_rect crop;
> +
> + /* Currently selected input on subdev */
> + int input;
> +
> + /* Buffer queue used in video-buf */
> + struct vb2_queue buffer_queue;
> + /* Queue of filled frames */
> + struct unicam_dmaqueue dma_queue;
> + /* IRQ lock for DMA queue */
> + spinlock_t dma_queue_lock;
> + /* lock used to access this structure */
> + struct mutex lock;
> + /* Flag to denote that we are processing buffers */
> + int streaming;
> +};
> +
> +/* Hardware access */
> +#define clk_write(dev, val) writel((val) | 0x5a000000, (dev)->clk_gate_base)
> +#define clk_read(dev) readl((dev)->clk_gate_base)
> +
> +#define reg_read(dev, offset) readl((dev)->base + (offset))
> +#define reg_write(dev, offset, val) writel(val, (dev)->base + (offset))
> +
> +#define reg_read_field(dev, offset, mask) get_field(reg_read((dev), (offset), \
> + mask))
> +
> +static inline int get_field(u32 value, u32 mask)
> +{
> + return (value & mask) >> __ffs(mask);
> +}
> +
> +static inline void set_field(u32 *valp, u32 field, u32 mask)
> +{
> + u32 val = *valp;
> +
> + val &= ~mask;
> + val |= (field << __ffs(mask)) & mask;
> + *valp = val;
> +}
> +
> +static inline void reg_write_field(struct unicam_cfg *dev, u32 offset,
> + u32 field, u32 mask)
> +{
> + u32 val = reg_read((dev), (offset));
> +
> + set_field(&val, field, mask);
> + reg_write((dev), (offset), val);
> +}
> +
> +/* Power management functions */
> +static inline int unicam_runtime_get(struct unicam_device *dev)
> +{
> + int r;
> +
> + r = pm_runtime_get_sync(&dev->pdev->dev);
> +
> + return r;
> +}
> +
> +static inline void unicam_runtime_put(struct unicam_device *dev)
> +{
> + pm_runtime_put_sync(&dev->pdev->dev);
> +}
> +
> +/* Format setup functions */
> +static int find_mbus_depth_by_code(u32 code)
> +{
> + const struct unicam_fmt *fmt;
> + unsigned int k;
> +
> + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> + fmt = &formats[k];
> + if (fmt->code == code)
> + return fmt->depth;
> + }
> +
> + return 0;
> +}
> +
> +static const struct unicam_fmt *find_format_by_code(struct unicam_device *dev,
> + u32 code)
> +{
> + const struct unicam_fmt *fmt;
> + unsigned int k;
> +
> + for (k = 0; k < dev->num_active_fmt; k++) {
> + fmt = &dev->active_fmts[k];
> + if (fmt->code == code)
> + return fmt;
> + }
> +
> + return NULL;
> +}
> +
> +static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev,
> + u32 pixelformat)
> +{
> + const struct unicam_fmt *fmt;
> + unsigned int k;
> +
> + for (k = 0; k < dev->num_active_fmt; k++) {
> + fmt = &dev->active_fmts[k];
> + if (fmt->fourcc == pixelformat)
> + return fmt;
> + }
> +
> + return NULL;
> +}
> +
> +static void dump_active_formats(struct unicam_device *dev)
> +{
> + int i;
> + char fourcc_str[V4L2_FOURCC_MAX_SIZE];
Swap these lines. A short line followed by a long line looks odd and there
is no reason for it in this case.
> +
> + for (i = 0; i < dev->num_active_fmt; i++) {
> + unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04X, fourcc %s, depth %d\n",
> + i, &dev->active_fmts[i], dev->active_fmts[i].code,
> + v4l2_fourcc2s(dev->active_fmts[i].fourcc,
> + fourcc_str),
> + dev->active_fmts[i].depth);
> + }
> +}
> +
> +static inline unsigned int bytes_per_line(u32 width,
> + const struct unicam_fmt *fmt)
> +{
> + return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT);
Two spaces before BPL, should be one.
> +}
> +
> +static int __subdev_get_format(struct unicam_device *dev,
> + struct v4l2_mbus_framefmt *fmt)
> +{
> + struct v4l2_subdev_format sd_fmt = {0};
> + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> + int ret;
> +
> + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + sd_fmt.pad = 0;
> +
> + ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_config,
> + &sd_fmt);
> + if (ret < 0)
> + return ret;
> +
> + if (mbus_fmt->field != V4L2_FIELD_NONE) {
> + /*
> + * No support for receiving interlaced video, so try to
> + * disable it if reported.
> + */
> + mbus_fmt->field = V4L2_FIELD_NONE;
> + ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
> + dev->sensor_config,
> + &sd_fmt);
You're not checking ret here?
Anyway, this is wrong. get_fmt should never change the format of the
subdev. The initial sensor format that is chosen by the driver when it
is loaded should never be interlaced, and when enumerating formats all
interlaced formats must be skipped.
> + }
> + *fmt = *mbus_fmt;
> +
> + unicam_dbg(1, dev, "%s %dx%d code:%04X\n", __func__,
> + fmt->width, fmt->height, fmt->code);
> +
> + return 0;
> +}
> +
> +static int __subdev_set_format(struct unicam_device *dev,
> + struct v4l2_mbus_framefmt *fmt)
> +{
> + struct v4l2_subdev_format sd_fmt = {
> + .which = V4L2_SUBDEV_FORMAT_ACTIVE,
> + };
> + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> + int ret;
> +
> + *mbus_fmt = *fmt;
> +
> + ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
> + &sd_fmt);
> + if (ret < 0)
> + return ret;
> +
> + unicam_dbg(1, dev, "%s %dx%d code:%04X\n", __func__,
> + fmt->width, fmt->height, fmt->code);
> +
> + return 0;
> +}
> +
> +static int unicam_calc_format_size_bpl(struct unicam_device *dev,
> + const struct unicam_fmt *fmt,
> + struct v4l2_format *f)
> +{
> + unsigned int min_bytesperline;
> + char fourcc_str[V4L2_FOURCC_MAX_SIZE];
Swap these two lines.
> +
> + if (!fmt) {
> + unicam_dbg(3, dev, "No unicam_fmt provided!\n");
> + return -EINVAL;
Can this ever happen?
> + }
> +
> + v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
> + &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0,
> + 0);
> +
> + min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt);
> +
> + if (f->fmt.pix.bytesperline > min_bytesperline &&
> + f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
> + f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
> + BPL_ALIGNMENT);
> + else
> + f->fmt.pix.bytesperline = min_bytesperline;
> +
> + /* Align height up for compatibility with other hardware blocks */
> + f->fmt.pix.sizeimage = ALIGN(f->fmt.pix.height, HEIGHT_ALIGNMENT) *
> + f->fmt.pix.bytesperline;
> +
> + unicam_dbg(3, dev, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
> + __func__, v4l2_fourcc2s(f->fmt.pix.pixelformat, fourcc_str),
> + f->fmt.pix.width, f->fmt.pix.height,
> + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
> +
> + return 0;
> +}
> +
> +static int unicam_reset_format(struct unicam_device *dev)
> +{
> + struct v4l2_mbus_framefmt mbus_fmt;
> + int ret;
> +
> + ret = __subdev_get_format(dev, &mbus_fmt);
> + if (ret) {
> + unicam_err(dev, "Failed to get_format - ret %d\n", ret);
> + return ret;
> + }
> +
> + if (mbus_fmt.code != dev->fmt->code) {
> + unicam_err(dev, "code mismatch - fmt->code %08X, mbus_fmt.code %08X\n",
Why 'X' instead of 'x'?
> + dev->fmt->code, mbus_fmt.code);
> + return ret;
> + }
> +
> + v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
> + dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +
> + unicam_calc_format_size_bpl(dev, dev->fmt, &dev->v_fmt);
> +
> + dev->m_fmt = mbus_fmt;
> +
> + return 0;
> +}
> +
> +static void unicam_wr_dma_addr(struct unicam_device *dev, unsigned int dmaaddr)
> +{
> + unicam_dbg(1, dev, "wr_dma_addr %08X-%08X\n",
> + dmaaddr, dmaaddr + dev->v_fmt.fmt.pix.sizeimage);
> + reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
> + reg_write(&dev->cfg,
> + UNICAM_IBEA0,
This can be joined with the reg_write line. No need to put this on a separate line.
> + 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));
16 << 10 is a bit magic. Should it be a define? Or at least add a comment.
> +}
> +
> +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;
Use dma_addr_t
> +
> + 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)
I'd join these lines. Or does this get over 80 cols?
> + unicam_process_buffer_complete(unicam);
> + }
> +
> + if (ista & (UNICAM_FSI | UNICAM_LCI)) {
> + spin_lock(&unicam->dma_queue_lock);
> + if (!list_empty(&dma_q->active) &&
> + unicam->cur_frm == unicam->next_frm)
> + unicam_schedule_next_buffer(unicam);
> + spin_unlock(&unicam->dma_queue_lock);
> + }
> +
> + if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) {
> + /* Switch out of trigger mode if selected */
> + reg_write_field(&unicam->cfg, UNICAM_ICTL, 1, UNICAM_TFC);
> + reg_write_field(&unicam->cfg, UNICAM_ICTL, 0, UNICAM_FCM);
> + }
> + return IRQ_HANDLED;
> +}
> +
> +static int unicam_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
> + strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
> +
> + snprintf(cap->bus_info, sizeof(cap->bus_info),
> + "platform:%s", dev->v4l2_dev.name);
> +
> + return 0;
> +}
> +
> +static int unicam_enum_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> + const struct unicam_fmt *fmt = NULL;
> +
> + if (f->index >= dev->num_active_fmt)
> + return -EINVAL;
> +
> + fmt = &dev->active_fmts[f->index];
> +
> + f->pixelformat = fmt->fourcc;
> +
> + return 0;
> +}
> +
> +static int unicam_g_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + *f = dev->v_fmt;
> +
> + return 0;
> +}
> +
> +static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> + const struct unicam_fmt *fmt;
> + struct v4l2_subdev_format sd_fmt = {
> + .which = V4L2_SUBDEV_FORMAT_TRY,
> + };
> + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> + int ret;
> +
> + fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
> + if (!fmt) {
> + unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use default of %08X\n",
> + f->fmt.pix.pixelformat, dev->active_fmts[0].fourcc);
> +
> + /* Just get the first one enumerated */
> + fmt = &dev->active_fmts[0];
> + f->fmt.pix.pixelformat = fmt->fourcc;
> + }
> +
> + v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
> + /*
> + * No support for receiving interlaced video, so never
> + * request it from the sensor subdev.
> + */
> + mbus_fmt->field = V4L2_FIELD_NONE;
> +
> + ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
> + &sd_fmt);
> + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
> + return ret;
> +
> + if (mbus_fmt->field != V4L2_FIELD_NONE)
> + unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
> +
> + v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
> + /*
> + * Use current colorspace for now, it will get
> + * updated properly during s_fmt
> + */
> + f->fmt.pix.colorspace = dev->v_fmt.fmt.pix.colorspace;
> + return unicam_calc_format_size_bpl(dev, fmt, f);
> +}
> +
> +static int unicam_s_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> + struct vb2_queue *q = &dev->buffer_queue;
> + const struct unicam_fmt *fmt;
> + struct v4l2_mbus_framefmt mbus_fmt = {0};
> + int ret;
> + char fourcc_str[V4L2_FOURCC_MAX_SIZE];
Swap these two lines.
> +
> + if (vb2_is_busy(q))
> + return -EBUSY;
> +
> + ret = unicam_try_fmt_vid_cap(file, priv, f);
> + if (ret < 0)
> + return ret;
> +
> + fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
> + if (!fmt) {
> + /* Unknown pixel format - adopt a default */
> + fmt = &dev->active_fmts[0];
> + f->fmt.pix.pixelformat = fmt->fourcc;
> + return -EINVAL;
> + }
> +
> + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
> +
> + ret = __subdev_set_format(dev, &mbus_fmt);
> + if (ret) {
> + unicam_dbg(3, dev,
> + "%s __subdev_set_format failed %d\n",
Can these two lines be joined?
> + __func__, ret);
> + return ret;
> + }
> +
> + /* Just double check nothing has gone wrong */
> + if (mbus_fmt.code != fmt->code) {
> + unicam_dbg(3, dev,
> + "%s subdev changed format on us, this should not happen\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + dev->fmt = fmt;
> + dev->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
> + dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
> + unicam_reset_format(dev);
> +
> + unicam_dbg(3, dev,
> + "%s %dx%d, mbus_fmt %08X, V4L2 pix %s.\n",
Ditto.
> + __func__,
> + dev->v_fmt.fmt.pix.width,
> + dev->v_fmt.fmt.pix.height,
Ditto.
> + mbus_fmt.code,
> + v4l2_fourcc2s(dev->v_fmt.fmt.pix.pixelformat,
> + fourcc_str));
Ditto.
> +
> + *f = dev->v_fmt;
> +
> + return 0;
> +}
> +
> +static int unicam_queue_setup(struct vb2_queue *vq,
> + unsigned int *nbuffers,
> + unsigned int *nplanes,
> + unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + struct unicam_device *dev = vb2_get_drv_priv(vq);
> + unsigned int size = dev->v_fmt.fmt.pix.sizeimage;
> +
> + if (vq->num_buffers + *nbuffers < 3)
> + *nbuffers = 3 - vq->num_buffers;
> +
> + if (*nplanes) {
> + if (sizes[0] < size) {
> + unicam_err(dev, "sizes[0] %i < size %u\n",
> + sizes[0], size);
> + return -EINVAL;
> + }
> + size = sizes[0];
> + }
> +
> + *nplanes = 1;
> + sizes[0] = size;
> +
> + return 0;
> +}
> +
> +static int unicam_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
> + struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
> + vb.vb2_buf);
> + unsigned long size;
> +
> + if (WARN_ON(!dev->fmt))
> + return -EINVAL;
> +
> + size = dev->v_fmt.fmt.pix.sizeimage;
> + if (vb2_plane_size(vb, 0) < size) {
> + unicam_err(dev, "data will not fit into plane (%lu < %lu)\n",
> + vb2_plane_size(vb, 0), size);
> + return -EINVAL;
> + }
> +
> + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
> + return 0;
> +}
> +
> +static void unicam_buffer_queue(struct vb2_buffer *vb)
> +{
> + struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
> + struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
> + vb.vb2_buf);
> + struct unicam_dmaqueue *dma_queue = &dev->dma_queue;
> + unsigned long flags = 0;
> +
> + /* recheck locking */
> + spin_lock_irqsave(&dev->dma_queue_lock, flags);
> + list_add_tail(&buf->list, &dma_queue->active);
> + spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
> +}
> +
> +static void unicam_wr_dma_config(struct unicam_device *dev,
> + unsigned int stride)
> +{
> + reg_write(&dev->cfg, UNICAM_IBLS, stride);
> +}
> +
> +static void unicam_set_packing_config(struct unicam_device *dev)
> +{
> + int pack, unpack;
> + u32 val;
> + int mbus_depth = find_mbus_depth_by_code(dev->fmt->code);
> + int v4l2_depth = dev->fmt->depth;
> +
> + if (mbus_depth == v4l2_depth) {
> + unpack = UNICAM_PUM_NONE;
> + pack = UNICAM_PPM_NONE;
> + } else {
> + switch (mbus_depth) {
> + case 8:
> + unpack = UNICAM_PUM_UNPACK8;
> + break;
> + case 10:
> + unpack = UNICAM_PUM_UNPACK10;
> + break;
> + case 12:
> + unpack = UNICAM_PUM_UNPACK12;
> + break;
> + case 14:
> + unpack = UNICAM_PUM_UNPACK14;
> + break;
> + case 16:
> + unpack = UNICAM_PUM_UNPACK16;
> + break;
> + default:
> + unpack = UNICAM_PUM_NONE;
> + break;
> + }
> + switch (v4l2_depth) {
> + case 8:
> + pack = UNICAM_PPM_PACK8;
> + break;
> + case 10:
> + pack = UNICAM_PPM_PACK10;
> + break;
> + case 12:
> + pack = UNICAM_PPM_PACK12;
> + break;
> + case 14:
> + pack = UNICAM_PPM_PACK14;
> + break;
> + case 16:
> + pack = UNICAM_PPM_PACK16;
> + break;
> + default:
> + pack = UNICAM_PPM_NONE;
> + break;
> + }
> + }
> +
> + val = 0;
> + set_field(&val, 2, UNICAM_DEBL_MASK);
> + set_field(&val, unpack, UNICAM_PUM_MASK);
> + set_field(&val, pack, UNICAM_PPM_MASK);
> + reg_write(&dev->cfg, UNICAM_IPIPE, val);
> +}
> +
> +static void unicam_cfg_image_id(struct unicam_device *dev)
> +{
> + struct unicam_cfg *cfg = &dev->cfg;
> +
> + if (dev->bus_type == V4L2_MBUS_CSI2) {
> + /* CSI2 mode */
> + reg_write(cfg, UNICAM_IDI0,
> + (dev->virtual_channel << 6) |
> + dev->fmt->csi_dt);
> + } else {
> + /* CCP2 mode */
> + reg_write(cfg, UNICAM_IDI0,
> + (0x80 | dev->fmt->csi_dt));
I'm sure these two lines can be joined as well. I'm not going to comment
on this any further, just take a look for unnecessary line breaks for v4.
> + }
> +}
> +
> +void unicam_start_rx(struct unicam_device *dev, unsigned long addr)
> +{
> + u32 val;
> + unsigned int i;
> + struct unicam_cfg *cfg = &dev->cfg;
> + int line_int_freq = dev->v_fmt.fmt.pix.height >> 2;
> +
> + if (line_int_freq < 128)
> + line_int_freq = 128;
> +
> + /* Enable lane clocks */
> + val = 1;
> + for (i = 0; i < dev->active_data_lanes; i++)
> + val = val << 2 | 1;
> + clk_write(cfg, val);
> +
> + /* Basic init */
> + reg_write(cfg, UNICAM_CTRL, UNICAM_MEM);
> +
> + /* Enable analogue control, and leave in reset. */
> + val = UNICAM_AR;
> + set_field(&val, 7, UNICAM_CTATADJ_MASK);
> + set_field(&val, 7, UNICAM_PTATADJ_MASK);
> + reg_write(cfg, UNICAM_ANA, val);
> + usleep_range(1000, 2000);
> +
> + /* Come out of reset */
> + reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_AR);
> +
> + /* Peripheral reset */
> + reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
> + reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
> +
> + reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
> +
> + /* Enable Rx control. */
> + val = reg_read(cfg, UNICAM_CTRL);
> + if (dev->bus_type == V4L2_MBUS_CSI2) {
> + set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
> + set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK);
> + } else {
> + set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK);
> + set_field(&val, dev->bus_flags, UNICAM_DCM_MASK);
> + }
> + set_field(&val, 0xF, UNICAM_PFT_MASK);
> + set_field(&val, 128, UNICAM_OET_MASK);
> + reg_write(cfg, UNICAM_CTRL, val);
> +
> + reg_write(cfg, UNICAM_IHWIN, 0);
> + reg_write(cfg, UNICAM_IVWIN, 0);
> +
> + val = reg_read(&dev->cfg, UNICAM_PRI);
> + set_field(&val, 0, UNICAM_BL_MASK);
> + set_field(&val, 0, UNICAM_BS_MASK);
> + set_field(&val, 0xE, UNICAM_PP_MASK);
> + set_field(&val, 8, UNICAM_NP_MASK);
> + set_field(&val, 2, UNICAM_PT_MASK);
> + set_field(&val, 1, UNICAM_PE);
Lots of magic numbers here. At the very least add some comments.
> + reg_write(cfg, UNICAM_PRI, val);
> +
> + reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_DDL);
> +
> + /* Always start in trigger frame capture mode (UNICAM_FCM set) */
> + val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM;
> + set_field(&val, line_int_freq, UNICAM_LCIE_MASK);
> + reg_write(cfg, UNICAM_ICTL, val);
> + reg_write(cfg, UNICAM_STA, UNICAM_STA_MASK_ALL);
> + reg_write(cfg, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL);
> +
> + /* tclk_term_en */
> + reg_write_field(cfg, UNICAM_CLT, 2, UNICAM_CLT1_MASK);
> + /* tclk_settle */
> + reg_write_field(cfg, UNICAM_CLT, 6, UNICAM_CLT2_MASK);
> + /* td_term_en */
> + reg_write_field(cfg, UNICAM_DLT, 2, UNICAM_DLT1_MASK);
> + /* ths_settle */
> + reg_write_field(cfg, UNICAM_DLT, 6, UNICAM_DLT2_MASK);
> + /* trx_enable */
> + reg_write_field(cfg, UNICAM_DLT, 0, UNICAM_DLT3_MASK);
> +
> + reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_SOE);
> +
> + val = 0;
> + set_field(&val, 1, UNICAM_PCE);
> + set_field(&val, 1, UNICAM_GI);
> + set_field(&val, 1, UNICAM_CPH);
> + set_field(&val, 0, UNICAM_PCVC_MASK);
> + set_field(&val, 1, UNICAM_PCDT_MASK);
> + reg_write(cfg, UNICAM_CMP0, val);
> +
> + /* Enable clock lane */
> + val = 0;
> + if (dev->bus_type == V4L2_MBUS_CSI2) {
> + /* CSI2 */
> + set_field(&val, 1, UNICAM_CLE);
> + set_field(&val, 1, UNICAM_CLLPE);
> + if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
> + set_field(&val, 1, UNICAM_CLTRE);
> + set_field(&val, 1, UNICAM_CLHSE);
> + }
> + } else {
> + /* CCP2 */
> + set_field(&val, 1, UNICAM_CLE);
> + set_field(&val, 1, UNICAM_CLHSE);
> + set_field(&val, 1, UNICAM_CLTRE);
> + }
> + reg_write(cfg, UNICAM_CLK, val);
> +
> + /* Enable required data lanes */
> + val = 0;
> + if (dev->bus_type == V4L2_MBUS_CSI2) {
> + /* CSI2 */
> + set_field(&val, 1, UNICAM_DLE);
> + set_field(&val, 1, UNICAM_DLLPE);
> + if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
> + set_field(&val, 1, UNICAM_DLTRE);
> + set_field(&val, 1, UNICAM_DLHSE);
> + }
> + } else {
> + /* CCP2 */
> + set_field(&val, 1, UNICAM_DLE);
> + set_field(&val, 1, UNICAM_DLHSE);
> + set_field(&val, 1, UNICAM_DLTRE);
> + }
> + reg_write(cfg, UNICAM_DAT0, val);
> +
> + if (dev->active_data_lanes == 1)
> + val = 0;
> + reg_write(cfg, UNICAM_DAT1, val);
> +
> + if (dev->max_data_lanes > 2) {
> + if (dev->active_data_lanes == 2)
> + val = 0;
> + reg_write(cfg, UNICAM_DAT2, val);
> +
> + if (dev->active_data_lanes == 3)
> + val = 0;
> + reg_write(cfg, UNICAM_DAT3, val);
> + }
> +
> + unicam_wr_dma_config(dev, dev->v_fmt.fmt.pix.bytesperline);
> + unicam_wr_dma_addr(dev, addr);
> + unicam_set_packing_config(dev);
> + unicam_cfg_image_id(dev);
> +
> + val = 0;
> + set_field(&val, 0, UNICAM_EDL_MASK);
> + reg_write(cfg, UNICAM_DCS, val);
> +
> + val = reg_read(cfg, UNICAM_MISC);
> + set_field(&val, 1, UNICAM_FL0);
> + set_field(&val, 1, UNICAM_FL1);
> + reg_write(cfg, UNICAM_MISC, val);
> +
> + reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE);
> +
> + reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
> +
> + reg_write_field(cfg, UNICAM_DCS, 1, UNICAM_LDP);
> +
> + /*
> + * Enable trigger only for the first frame to
> + * sync correctly to the FS from the source.
> + */
> + reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_TFC);
This function really could do with some more comments.
> +}
> +
> +static void unicam_disable(struct unicam_device *dev)
> +{
> + struct unicam_cfg *cfg = &dev->cfg;
> +
> + /* Analogue lane control disable */
> + reg_write_field(cfg, UNICAM_ANA, 1, UNICAM_DDL);
> +
> + /* Stop the output engine */
> + reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_SOE);
> +
> + /* Disable the data lanes. */
> + reg_write(cfg, UNICAM_DAT0, 0);
> + reg_write(cfg, UNICAM_DAT1, 0);
> +
> + if (dev->max_data_lanes > 2) {
> + reg_write(cfg, UNICAM_DAT2, 0);
> + reg_write(cfg, UNICAM_DAT3, 0);
> + }
> +
> + /* Peripheral reset */
> + reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
> + usleep_range(50, 100);
> + reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
> +
> + /* Disable peripheral */
> + reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
> +
> + /* Disable all lane clocks */
> + clk_write(cfg, 0);
> +}
> +
> +static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct unicam_device *dev = vb2_get_drv_priv(vq);
> + struct unicam_dmaqueue *dma_q = &dev->dma_queue;
> + struct unicam_buffer *buf, *tmp;
> + unsigned long addr = 0;
> + unsigned long flags;
> + int ret;
> +
> + spin_lock_irqsave(&dev->dma_queue_lock, flags);
> + buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
> + dev->cur_frm = buf;
> + dev->next_frm = buf;
> + list_del(&buf->list);
> + spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
> +
> + addr = vb2_dma_contig_plane_dma_addr(&dev->cur_frm->vb.vb2_buf, 0);
> + dev->sequence = 0;
> +
> + ret = unicam_runtime_get(dev);
> + if (ret < 0) {
> + unicam_dbg(3, dev, "unicam_runtime_get failed\n");
> + goto err_release_buffers;
> + }
> +
> + dev->active_data_lanes = dev->max_data_lanes;
> + if (dev->bus_type == V4L2_MBUS_CSI2 &&
> + v4l2_subdev_has_op(dev->sensor, video, g_mbus_config)) {
> + struct v4l2_mbus_config mbus_config;
> +
> + ret = v4l2_subdev_call(dev->sensor, video, g_mbus_config,
> + &mbus_config);
> + if (ret < 0) {
> + unicam_dbg(3, dev, "g_mbus_config failed\n");
> + goto err_pm_put;
> + }
> +
> + switch (mbus_config.flags & V4L2_MBUS_CSI2_LANES) {
> + case V4L2_MBUS_CSI2_1_LANE:
> + dev->active_data_lanes = 1;
> + break;
> + case V4L2_MBUS_CSI2_2_LANE:
> + dev->active_data_lanes = 2;
> + break;
> + case V4L2_MBUS_CSI2_3_LANE:
> + dev->active_data_lanes = 3;
> + break;
> + case V4L2_MBUS_CSI2_4_LANE:
> + dev->active_data_lanes = 4;
> + break;
I assume this will change in v4, using Philipp Zabel's new V4L2_MBUS_CSI2_LANE_MASK
define. The only thing you should use from mbus_config is V4L2_MBUS_CSI2_LANE_MASK,
everything else should be ignored since it comes from the DT.
> + default:
> + unicam_err(dev, "Invalid CSI2 lane flag value - %X\n",
> + mbus_config.flags & V4L2_MBUS_CSI2_LANES);
> + break;
> + }
> +
> + if ((mbus_config.flags ^ dev->bus_flags) &
> + (V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
> + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK))
> + unicam_err(dev, "g_mbus_format returned different clocking mode to DT\n");
> + }
> + if (dev->active_data_lanes > dev->max_data_lanes) {
> + unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n",
> + dev->active_data_lanes,
> + dev->max_data_lanes);
> + ret = -EINVAL;
> + goto err_pm_put;
> + }
> +
> + unicam_dbg(1, dev, "Running with %u data lanes\n",
> + dev->active_data_lanes);
> +
> + ret = clk_set_rate(dev->clock, 100 * 1000 * 1000);
> + if (ret) {
> + unicam_err(dev, "failed to set up clock\n");
> + goto err_pm_put;
> + }
> +
> + ret = clk_prepare_enable(dev->clock);
> + if (ret) {
> + unicam_err(dev, "Failed to enable CSI clock: %d\n", ret);
> + goto err_pm_put;
> + }
> + ret = v4l2_subdev_call(dev->sensor, core, s_power, 1);
> + if (ret < 0 && ret != -ENOIOCTLCMD) {
> + unicam_err(dev, "power on failed in subdev\n");
> + goto err_clock_unprepare;
> + }
> + dev->streaming = 1;
> +
> + unicam_start_rx(dev, addr);
> +
> + ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
> + if (ret < 0) {
> + unicam_err(dev, "stream on failed in subdev\n");
> + goto err_disable_unicam;
> + }
> +
> + return 0;
> +
> +err_disable_unicam:
> + unicam_disable(dev);
> + v4l2_subdev_call(dev->sensor, core, s_power, 0);
> +err_clock_unprepare:
> + clk_disable_unprepare(dev->clock);
> +err_pm_put:
> + unicam_runtime_put(dev);
> +err_release_buffers:
> + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
> + }
> + if (dev->cur_frm != dev->next_frm)
> + vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
> + VB2_BUF_STATE_QUEUED);
> + vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
> + dev->next_frm = NULL;
> + dev->cur_frm = NULL;
> +
> + return ret;
> +}
> +
> +static void unicam_stop_streaming(struct vb2_queue *vq)
> +{
> + struct unicam_device *dev = vb2_get_drv_priv(vq);
> + struct unicam_dmaqueue *dma_q = &dev->dma_queue;
> + struct unicam_buffer *buf, *tmp;
> + unsigned long flags;
> +
> + if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0)
> + unicam_err(dev, "stream off failed in subdev\n");
> +
> + unicam_disable(dev);
> +
> + /* Release all active buffers */
> + spin_lock_irqsave(&dev->dma_queue_lock, flags);
> + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> + }
> +
> + if (dev->cur_frm == dev->next_frm) {
> + vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> + } else {
> + vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> + vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
> + VB2_BUF_STATE_ERROR);
> + }
> + dev->cur_frm = NULL;
> + dev->next_frm = NULL;
> + spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
> +
> + if (v4l2_subdev_has_op(dev->sensor, core, s_power)) {
> + if (v4l2_subdev_call(dev->sensor, core, s_power, 0) < 0)
> + unicam_err(dev, "power off failed in subdev\n");
> + }
> +
> + clk_disable_unprepare(dev->clock);
> + unicam_runtime_put(dev);
> +}
> +
> +static int unicam_enum_input(struct file *file, void *priv,
> + struct v4l2_input *inp)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + if (inp->index != 0)
> + return -EINVAL;
> +
> + inp->type = V4L2_INPUT_TYPE_CAMERA;
> + if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) {
> + inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
> + inp->std = 0;
> + } else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) {
> + inp->capabilities = V4L2_IN_CAP_STD;
> + if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std)
> + < 0)
> + inp->std = V4L2_STD_ALL;
> + } else {
> + inp->capabilities = 0;
> + inp->std = 0;
> + }
> + sprintf(inp->name, "Camera 0");
> + return 0;
> +}
> +
> +static int unicam_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> + *i = 0;
> +
> + return 0;
> +}
> +
> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
> +{
> + /*
> + * FIXME: Ideally we would like to be able to query the sensor
You probably mean 'subdev' instead of 'sensor'. A sensor has no 'inputs'
as such.
> + * for information over the input connectors it supports, and
> + * map that through in to a call to video_ops->s_routing.
> + * There is no infrastructure support for defining that within
> + * devicetree at present. Until that is implemented we can't
> + * map a user physical connector number to s_routing input number.
> + */
> + if (i > 0)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int unicam_querystd(struct file *file, void *priv,
> + v4l2_std_id *std)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, video, querystd, std);
> +}
> +
> +static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, video, g_std, std);
> +}
> +
> +static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> + int ret;
> + v4l2_std_id current_std;
> +
> + ret = v4l2_subdev_call(dev->sensor, video, g_std, ¤t_std);
> + if (ret)
> + return ret;
> +
> + if (std == current_std)
> + return 0;
> +
> + if (vb2_is_busy(&dev->buffer_queue))
> + return -EBUSY;
> +
> + ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
> +
> + /* Force recomputation of bytesperline */
> + dev->v_fmt.fmt.pix.bytesperline = 0;
> +
> + unicam_reset_format(dev);
> +
> + return ret;
> +}
> +
> +static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
> +}
> +
> +static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
> +}
> +
> +static int unicam_g_dv_timings(struct file *file, void *priv,
> + struct v4l2_dv_timings *timings)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
> +}
> +
> +static int unicam_s_dv_timings(struct file *file, void *priv,
> + struct v4l2_dv_timings *timings)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> + struct v4l2_dv_timings current_timings;
> + int ret;
> +
> + ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings,
> + ¤t_timings);
> +
> + if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false))
> + return 0;
> +
> + if (vb2_is_busy(&dev->buffer_queue))
> + return -EBUSY;
> +
> + ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
> +
> + /* Force recomputation of bytesperline */
> + dev->v_fmt.fmt.pix.bytesperline = 0;
> +
> + unicam_reset_format(dev);
> +
> + return ret;
> +}
> +
> +static int unicam_query_dv_timings(struct file *file, void *priv,
> + struct v4l2_dv_timings *timings)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
> +}
> +
> +static int unicam_enum_dv_timings(struct file *file, void *priv,
> + struct v4l2_enum_dv_timings *timings)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
> +}
> +
> +static int unicam_dv_timings_cap(struct file *file, void *priv,
> + struct v4l2_dv_timings_cap *cap)
> +{
> + struct unicam_device *dev = video_drvdata(file);
> +
> + return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
> +}
> +
> +static int unicam_subscribe_event(struct v4l2_fh *fh,
> + const struct v4l2_event_subscription *sub)
> +{
> + switch (sub->type) {
> + case V4L2_EVENT_SOURCE_CHANGE:
> + return v4l2_event_subscribe(fh, sub, 4, NULL);
> + }
> +
> + return v4l2_ctrl_subscribe_event(fh, sub);
> +}
> +
> +static int unicam_log_status(struct file *file, void *fh)
> +{
> + struct unicam_device *dev = video_drvdata(file);
Add newline.
> + /* status for sub devices */
> + v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status);
> +
> + return 0;
> +}
> +
> +static void unicam_notify(struct v4l2_subdev *sd,
> + unsigned int notification, void *arg)
> +{
> + struct unicam_device *dev =
> + container_of(sd->v4l2_dev, struct unicam_device, v4l2_dev);
> +
> + switch (notification) {
> + case V4L2_DEVICE_NOTIFY_EVENT:
> + v4l2_event_queue(&dev->video_dev, arg);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static const struct vb2_ops unicam_video_qops = {
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .queue_setup = unicam_queue_setup,
> + .buf_prepare = unicam_buffer_prepare,
> + .buf_queue = unicam_buffer_queue,
> + .start_streaming = unicam_start_streaming,
> + .stop_streaming = unicam_stop_streaming,
> +};
> +
> +/* unicam capture driver file operations */
> +static const struct v4l2_file_operations unicam_fops = {
> + .owner = THIS_MODULE,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .read = vb2_fop_read,
> + .poll = vb2_fop_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = vb2_fop_mmap,
> +};
> +
> +/* unicam capture ioctl operations */
> +static const struct v4l2_ioctl_ops unicam_ioctl_ops = {
> + .vidioc_querycap = unicam_querycap,
> + .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid_cap,
> + .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid_cap,
> + .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap,
> + .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap,
> +
> + .vidioc_enum_input = unicam_enum_input,
> + .vidioc_g_input = unicam_g_input,
> + .vidioc_s_input = unicam_s_input,
> +
> + .vidioc_querystd = unicam_querystd,
> + .vidioc_s_std = unicam_s_std,
> + .vidioc_g_std = unicam_g_std,
> +
> + .vidioc_g_edid = unicam_g_edid,
> + .vidioc_s_edid = unicam_s_edid,
> +
> + .vidioc_s_dv_timings = unicam_s_dv_timings,
> + .vidioc_g_dv_timings = unicam_g_dv_timings,
> + .vidioc_query_dv_timings = unicam_query_dv_timings,
> + .vidioc_enum_dv_timings = unicam_enum_dv_timings,
> + .vidioc_dv_timings_cap = unicam_dv_timings_cap,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +
> + .vidioc_log_status = unicam_log_status,
> + .vidioc_subscribe_event = unicam_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static int
> +unicam_async_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *subdev,
> + struct v4l2_async_subdev *asd)
> +{
> + struct unicam_device *unicam = container_of(notifier->v4l2_dev,
> + struct unicam_device, v4l2_dev);
> + struct v4l2_subdev_mbus_code_enum mbus_code;
> + int j;
> + struct unicam_fmt *active_fmt;
> + int ret = 0;
> +
> + if (unicam->sensor) {
> + unicam_info(unicam, "Rejecting subdev %s (Already set!!)",
> + subdev->name);
> + return 0;
> + }
> +
> + unicam->sensor = subdev;
> + unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name);
> +
> + /* Enumerate sub device formats and enable all matching local formats */
> + unicam->num_active_fmt = 0;
> + active_fmt = &unicam->active_fmts[0];
> + unicam_dbg(2, unicam, "Get supported formats...\n");
> + for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
> + const struct unicam_fmt *fmt = NULL;
> + int k;
> +
> + memset(&mbus_code, 0, sizeof(mbus_code));
> + mbus_code.index = j;
> + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
> + NULL, &mbus_code);
> + if (ret < 0) {
> + unicam_dbg(2, unicam,
> + "subdev->enum_mbus_code idx %d returned %d - continue\n",
> + j, ret);
> + continue;
> + }
> +
> + unicam_dbg(2, unicam,
> + "subdev %s: code: %04x idx: %d\n",
> + subdev->name, mbus_code.code, j);
> +
> + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> + if (mbus_code.code == formats[k].code) {
> + fmt = &formats[k];
> + break;
> + }
> + }
> + unicam_dbg(2, unicam, "fmt %04x returned as %p, V4L2 FOURCC %04x, csi_dt %02X\n",
> + mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
> + fmt ? fmt->csi_dt : 0);
> + if (fmt) {
> + const struct bayer_fmt *fmt_list = NULL;
> +
> + switch (fmt->fourcc) {
> + case PIX_FMT_ALL_BGGR:
> + fmt_list = all_bayer_bggr;
> + break;
> + case PIX_FMT_ALL_RGGB:
> + fmt_list = all_bayer_rggb;
> + break;
> + case PIX_FMT_ALL_GBRG:
> + fmt_list = all_bayer_gbrg;
> + break;
> + case PIX_FMT_ALL_GRBG:
> + fmt_list = all_bayer_grbg;
> + break;
> + }
> + if (fmt_list) {
> + for (k = 0; fmt_list[k].fourcc; k++) {
> + char fourcc_str[V4L2_FOURCC_MAX_SIZE];
> +
> + *active_fmt = *fmt;
> + active_fmt->fourcc = fmt_list[k].fourcc;
> + active_fmt->depth = fmt_list[k].depth;
> + unicam_dbg(2, unicam,
> + "matched fourcc: %s: code: %04x idx: %d\n",
> + v4l2_fourcc2s(fmt->fourcc,
> + fourcc_str),
> + fmt->code,
> + unicam->num_active_fmt);
> + unicam->num_active_fmt++;
> + active_fmt++;
> + }
> + } else {
> + char fourcc_str[V4L2_FOURCC_MAX_SIZE];
> +
> + *active_fmt = *fmt;
> + unicam_dbg(2, unicam,
> + "matched fourcc: %s: code: %04x idx: %d\n",
> + v4l2_fourcc2s(fmt->fourcc,
> + fourcc_str),
> + fmt->code,
> + unicam->num_active_fmt);
> + unicam->num_active_fmt++;
> + active_fmt++;
> + }
> + }
> + }
> + unicam_dbg(2, unicam,
> + "Done all formats\n");
> + dump_active_formats(unicam);
> +
> + return 0;
> +}
> +
> +static int unicam_probe_complete(struct unicam_device *unicam)
> +{
> + struct video_device *vdev;
> + struct vb2_queue *q;
> + struct v4l2_mbus_framefmt mbus_fmt = {0};
> + const struct unicam_fmt *fmt;
> + int ret;
> +
> + v4l2_set_subdev_hostdata(unicam->sensor, unicam);
> +
> + unicam->v4l2_dev.notify = unicam_notify;
> +
> + unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor);
> + if (!unicam->sensor_config)
> + return -ENOMEM;
> +
> + ret = __subdev_get_format(unicam, &mbus_fmt);
> + if (ret) {
> + unicam_err(unicam, "Failed to get_format - ret %d\n", ret);
> + return ret;
> + }
> +
> + fmt = find_format_by_code(unicam, mbus_fmt.code);
> + if (!fmt) {
> + unicam_dbg(3, unicam, "mbus code format (0x%08x) not found.\n",
> + mbus_fmt.code);
> + return -EINVAL;
> + }
This is weird code. What typically happens at this time is that the first
valid format (i.e. one that both the subdev driver and this driver support)
is chosen and set. What seems to happen here is that you get the current
subdev format and try to select that, but that format may not be valid
causing this probe to return an error.
Instead, just pick the first of the active formats and select that.
Only if there are no active formats at all (i.e. no matches were found)
would you return an error.
> + unicam->fmt = fmt;
> + unicam->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
> +
> + /* Read current subdev format */
> + unicam_reset_format(unicam);
> +
> + if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
> + v4l2_std_id tvnorms;
> +
> + if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video,
> + g_tvnorms)))
> + /*
> + * Subdevice should not advertise querystd but not
> + * g_tvnorms
You probably mean s_std instead of querystd?
> + */
> + return -EINVAL;
> +
> + ret = v4l2_subdev_call(unicam->sensor, video,
> + g_tvnorms, &tvnorms);
> + if (WARN_ON(ret))
> + return -EINVAL;
> + unicam->video_dev.tvnorms |= tvnorms;
> + }
> +
> + spin_lock_init(&unicam->dma_queue_lock);
> + mutex_init(&unicam->lock);
> +
> + /* Add controls from the subdevice */
> + ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler,
> + unicam->sensor->ctrl_handler, NULL);
> + if (ret < 0)
> + return ret;
> +
> + q = &unicam->buffer_queue;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
> + q->drv_priv = unicam;
> + q->ops = &unicam_video_qops;
> + q->mem_ops = &vb2_dma_contig_memops;
> + q->buf_struct_size = sizeof(struct unicam_buffer);
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + q->lock = &unicam->lock;
> + q->min_buffers_needed = 2;
> + q->dev = &unicam->pdev->dev;
> +
> + ret = vb2_queue_init(q);
> + if (ret) {
> + unicam_err(unicam, "vb2_queue_init() failed\n");
> + return ret;
> + }
> +
> + INIT_LIST_HEAD(&unicam->dma_queue.active);
> +
> + vdev = &unicam->video_dev;
> + strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name));
> + vdev->release = video_device_release_empty;
> + vdev->fops = &unicam_fops;
> + vdev->ioctl_ops = &unicam_ioctl_ops;
> + vdev->v4l2_dev = &unicam->v4l2_dev;
> + vdev->vfl_dir = VFL_DIR_RX;
> + vdev->queue = q;
> + vdev->lock = &unicam->lock;
> + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
> + V4L2_CAP_READWRITE;
> +
> + video_set_drvdata(vdev, unicam);
> + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> + if (ret) {
> + unicam_err(unicam,
> + "Unable to register video device.\n");
> + return ret;
> + }
> +
> + if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_STD);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_STD);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUMSTD);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERYSTD);
I would move QUERYSTD out of this if and test for it separately.
QUERYSTD is optional in SDTV receivers (unfortunately), so it may not
be there, even if S_STD is.
> + }
> + if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_EDID);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_EDID);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_DV_TIMINGS_CAP);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_DV_TIMINGS);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_DV_TIMINGS);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_DV_TIMINGS);
> + v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERY_DV_TIMINGS);
> + }
> +
> + ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev);
> + if (ret) {
> + unicam_err(unicam,
> + "Unable to register subdev nodes.\n");
> + video_unregister_device(&unicam->video_dev);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int unicam_async_complete(struct v4l2_async_notifier *notifier)
> +{
> + struct unicam_device *unicam = container_of(notifier->v4l2_dev,
> + struct unicam_device, v4l2_dev);
> +
> + return unicam_probe_complete(unicam);
> +}
> +
> +static int of_unicam_connect_subdevs(struct unicam_device *dev)
> +{
> + struct platform_device *pdev = dev->pdev;
> + struct device_node *parent, *ep_node = NULL, *remote_ep = NULL,
> + *sensor_node = NULL;
> + struct v4l2_fwnode_endpoint *ep;
> + struct v4l2_async_subdev *asd;
> + int ret = -EINVAL;
> + unsigned int lane;
> + struct v4l2_async_subdev **subdevs = NULL;
> + unsigned int peripheral_data_lanes;
> +
> + parent = pdev->dev.of_node;
> +
> + asd = &dev->asd;
> + ep = &dev->endpoint;
> +
> + ep_node = of_graph_get_next_endpoint(parent, NULL);
> + if (!ep_node) {
> + unicam_dbg(3, dev, "can't get next endpoint\n");
> + goto cleanup_exit;
> + }
> +
> + unicam_dbg(3, dev, "ep_node is %s\n",
> + ep_node->name);
> +
> + v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), ep);
> +
> + for (lane = 0; lane < ep->bus.mipi_csi2.num_data_lanes; lane++) {
> + if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
> + unicam_err(dev, "Local endpoint - data lane reordering not supported\n");
> + goto cleanup_exit;
> + }
> + }
> +
> + peripheral_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
> +
> + sensor_node = of_graph_get_remote_port_parent(ep_node);
> + if (!sensor_node) {
> + unicam_dbg(3, dev, "can't get remote parent\n");
> + goto cleanup_exit;
> + }
> + unicam_dbg(3, dev, "sensor_node is %s\n",
> + sensor_node->name);
> + asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
> + asd->match.fwnode.fwnode = of_fwnode_handle(sensor_node);
> +
> + remote_ep = of_graph_get_remote_endpoint(ep_node);
> + if (!remote_ep) {
> + unicam_dbg(3, dev, "can't get remote-endpoint\n");
> + goto cleanup_exit;
> + }
> + unicam_dbg(3, dev, "remote_ep is %s\n",
> + remote_ep->name);
> + v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), ep);
> + unicam_dbg(3, dev, "parsed remote_ep to endpoint. nr_of_link_frequencies %u, bus_type %u\n",
> + ep->nr_of_link_frequencies, ep->bus_type);
> +
> + switch (ep->bus_type) {
> + case V4L2_MBUS_CSI2:
> + if (ep->bus.mipi_csi2.num_data_lanes >
> + peripheral_data_lanes) {
> + unicam_err(dev, "Subdevice %s wants too many data lanes (%u > %u)\n",
> + sensor_node->name,
> + ep->bus.mipi_csi2.num_data_lanes,
> + peripheral_data_lanes);
> + goto cleanup_exit;
> + }
> + for (lane = 0;
> + lane < ep->bus.mipi_csi2.num_data_lanes;
> + lane++) {
> + if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
> + unicam_err(dev, "Subdevice %s - incompatible data lane config\n",
> + sensor_node->name);
> + goto cleanup_exit;
> + }
> + }
> + dev->max_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
> + dev->bus_flags = ep->bus.mipi_csi2.flags;
> + break;
> + case V4L2_MBUS_CCP2:
> + if (ep->bus.mipi_csi1.clock_lane != 0 ||
> + ep->bus.mipi_csi1.data_lane != 1) {
> + unicam_err(dev, "Subdevice %s incompatible lane config\n",
> + sensor_node->name);
> + goto cleanup_exit;
> + }
> + dev->max_data_lanes = 1;
> + dev->bus_flags = ep->bus.mipi_csi1.strobe;
> + break;
> + default:
> + /* Unsupported bus type */
> + unicam_err(dev, "sub-device %s is not a CSI2 or CCP2 device %d\n",
> + sensor_node->name, ep->bus_type);
> + goto cleanup_exit;
> + }
> +
> + /* Store bus type - CSI2 or CCP2 */
> + dev->bus_type = ep->bus_type;
> + unicam_dbg(3, dev, "bus_type is %d\n", dev->bus_type);
> +
> + /* Store Virtual Channel number */
> + dev->virtual_channel = ep->base.id;
> +
> + unicam_dbg(3, dev, "v4l2-endpoint: %s\n",
> + dev->bus_type == V4L2_MBUS_CSI2 ? "CSI2" : "CCP2");
> + unicam_dbg(3, dev, "Virtual Channel=%d\n", dev->virtual_channel);
> + if (dev->bus_type == V4L2_MBUS_CSI2)
> + unicam_dbg(3, dev, "flags=0x%08x\n",
> + ep->bus.mipi_csi2.flags);
> + unicam_dbg(3, dev, "num_data_lanes=%d\n", dev->max_data_lanes);
> +
> + unicam_dbg(1, dev, "found sub-device %s\n",
> + sensor_node->name);
> +
> + subdevs = devm_kzalloc(&dev->pdev->dev, sizeof(*subdevs), GFP_KERNEL);
> + if (!subdevs) {
> + ret = -ENOMEM;
> + goto cleanup_exit;
> + }
> + subdevs[0] = asd;
> + dev->notifier.subdevs = subdevs;
> + dev->notifier.num_subdevs = 1;
> + dev->notifier.bound = unicam_async_bound;
> + dev->notifier.complete = unicam_async_complete;
> + ret = v4l2_async_notifier_register(&dev->v4l2_dev,
> + &dev->notifier);
> + if (ret) {
> + unicam_err(dev, "Error registering async notifier - ret %d\n",
> + ret);
> + ret = -EINVAL;
> + }
> +
> +cleanup_exit:
> + if (remote_ep)
> + of_node_put(remote_ep);
> + if (sensor_node)
> + of_node_put(sensor_node);
> + if (ep_node)
> + of_node_put(ep_node);
> +
> + return ret;
> +}
> +
> +static int unicam_probe(struct platform_device *pdev)
> +{
> + struct unicam_cfg *unicam_cfg;
> + struct unicam_device *unicam;
> + struct v4l2_ctrl_handler *hdl;
> + struct resource *res;
> + int ret;
> +
> + unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
> + if (!unicam)
> + return -ENOMEM;
> +
> + unicam->pdev = pdev;
> + unicam_cfg = &unicam->cfg;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(unicam_cfg->base)) {
> + unicam_err(unicam, "Failed to get main io block\n");
> + return PTR_ERR(unicam_cfg->base);
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(unicam_cfg->clk_gate_base)) {
> + unicam_err(unicam, "Failed to get 2nd io block\n");
> + return PTR_ERR(unicam_cfg->clk_gate_base);
> + }
> +
> + unicam->clock = devm_clk_get(&pdev->dev, "lp");
> + 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;
> + }
> +
> + ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0,
> + "unicam_capture0", unicam);
> + if (ret) {
> + dev_err(&pdev->dev, "Unable to request interrupt\n");
> + return -EINVAL;
> + }
> +
> + ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
> + if (ret) {
> + unicam_err(unicam,
> + "Unable to register v4l2 device.\n");
> + return ret;
> + }
> +
> + /* Reserve space for the controls */
> + hdl = &unicam->ctrl_handler;
> + ret = v4l2_ctrl_handler_init(hdl, 16);
> + if (ret < 0)
> + goto probe_out_v4l2_unregister;
> + unicam->v4l2_dev.ctrl_handler = hdl;
> +
> + /* set the driver data in platform device */
> + platform_set_drvdata(pdev, unicam);
> +
> + ret = of_unicam_connect_subdevs(unicam);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to connect subdevs\n");
> + goto free_hdl;
> + }
> +
> + /* Enable the block power domain */
> + pm_runtime_enable(&pdev->dev);
> +
> + return 0;
> +
> +free_hdl:
> + v4l2_ctrl_handler_free(hdl);
> +probe_out_v4l2_unregister:
> + v4l2_device_unregister(&unicam->v4l2_dev);
> + return ret;
> +}
> +
> +static int unicam_remove(struct platform_device *pdev)
> +{
> + struct unicam_device *unicam = platform_get_drvdata(pdev);
> +
> + unicam_dbg(2, unicam, "%s\n", __func__);
> +
> + pm_runtime_disable(&pdev->dev);
> +
> + v4l2_async_notifier_unregister(&unicam->notifier);
> + v4l2_ctrl_handler_free(&unicam->ctrl_handler);
> + v4l2_device_unregister(&unicam->v4l2_dev);
> + video_unregister_device(&unicam->video_dev);
> + if (unicam->sensor_config)
> + v4l2_subdev_free_pad_config(unicam->sensor_config);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id unicam_of_match[] = {
> + { .compatible = "brcm,bcm2835-unicam", },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, unicam_of_match);
> +
> +static struct platform_driver unicam_driver = {
> + .probe = unicam_probe,
> + .remove = unicam_remove,
> + .driver = {
> + .name = UNICAM_MODULE_NAME,
> + .of_match_table = of_match_ptr(unicam_of_match),
> + },
> +};
> +
> +module_platform_driver(unicam_driver);
> +
> +MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.org>");
> +MODULE_DESCRIPTION("BCM2835 Unicam driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(UNICAM_VERSION);
> diff --git a/drivers/media/platform/bcm2835/vc4-regs-unicam.h b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
> new file mode 100644
> index 0000000..28e9a75
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
> @@ -0,0 +1,264 @@
> +/*
> + * Copyright (C) 2017 Raspberry Pi Trading.
> + * Dave Stevenson <dave.stevenson@raspberrypi.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#ifndef VC4_REGS_UNICAM_H
> +#define VC4_REGS_UNICAM_H
> +
> +/*
> + * The following values are taken from files found within the code drop
> + * made by Broadcom for the BCM21553 Graphics Driver, predominantly in
> + * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h.
> + * They have been modified to be only the register offset.
> + */
> +#define UNICAM_CTRL 0x000
> +#define UNICAM_STA 0x004
> +#define UNICAM_ANA 0x008
> +#define UNICAM_PRI 0x00c
> +#define UNICAM_CLK 0x010
> +#define UNICAM_CLT 0x014
> +#define UNICAM_DAT0 0x018
> +#define UNICAM_DAT1 0x01c
> +#define UNICAM_DAT2 0x020
> +#define UNICAM_DAT3 0x024
> +#define UNICAM_DLT 0x028
> +#define UNICAM_CMP0 0x02c
> +#define UNICAM_CMP1 0x030
> +#define UNICAM_CAP0 0x034
> +#define UNICAM_CAP1 0x038
> +#define UNICAM_ICTL 0x100
> +#define UNICAM_ISTA 0x104
> +#define UNICAM_IDI0 0x108
> +#define UNICAM_IPIPE 0x10c
> +#define UNICAM_IBSA0 0x110
> +#define UNICAM_IBEA0 0x114
> +#define UNICAM_IBLS 0x118
> +#define UNICAM_IBWP 0x11c
> +#define UNICAM_IHWIN 0x120
> +#define UNICAM_IHSTA 0x124
> +#define UNICAM_IVWIN 0x128
> +#define UNICAM_IVSTA 0x12c
> +#define UNICAM_ICC 0x130
> +#define UNICAM_ICS 0x134
> +#define UNICAM_IDC 0x138
> +#define UNICAM_IDPO 0x13c
> +#define UNICAM_IDCA 0x140
> +#define UNICAM_IDCD 0x144
> +#define UNICAM_IDS 0x148
> +#define UNICAM_DCS 0x200
> +#define UNICAM_DBSA0 0x204
> +#define UNICAM_DBEA0 0x208
> +#define UNICAM_DBWP 0x20c
> +#define UNICAM_DBCTL 0x300
> +#define UNICAM_IBSA1 0x304
> +#define UNICAM_IBEA1 0x308
> +#define UNICAM_IDI1 0x30c
> +#define UNICAM_DBSA1 0x310
> +#define UNICAM_DBEA1 0x314
> +#define UNICAM_MISC 0x400
> +
> +/*
> + * The following bitmasks are from the kernel released by Broadcom
> + * for Android - https://android.googlesource.com/kernel/bcm/
> + * The Rhea, Hawaii, and Java chips all contain the same VideoCore4
> + * Unicam block as BCM2835, as defined in eg
> + * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar.
> + * Values reworked to use the kernel BIT and GENMASK macros.
> + *
> + * Some of the bit mnenomics have been amended to match the datasheet.
> + */
> +/* UNICAM_CTRL Register */
> +#define UNICAM_CPE BIT(0)
> +#define UNICAM_MEM BIT(1)
> +#define UNICAM_CPR BIT(2)
> +#define UNICAM_CPM_MASK GENMASK(3, 3)
> +#define UNICAM_CPM_CSI2 0
> +#define UNICAM_CPM_CCP2 1
> +#define UNICAM_SOE BIT(4)
> +#define UNICAM_DCM_MASK GENMASK(5, 5)
> +#define UNICAM_DCM_STROBE 0
> +#define UNICAM_DCM_DATA 1
> +#define UNICAM_SLS BIT(6)
> +#define UNICAM_PFT_MASK GENMASK(11, 8)
> +#define UNICAM_OET_MASK GENMASK(20, 12)
> +
> +/* UNICAM_STA Register */
> +#define UNICAM_SYN BIT(0)
> +#define UNICAM_CS BIT(1)
> +#define UNICAM_SBE BIT(2)
> +#define UNICAM_PBE BIT(3)
> +#define UNICAM_HOE BIT(4)
> +#define UNICAM_PLE BIT(5)
> +#define UNICAM_SSC BIT(6)
> +#define UNICAM_CRCE BIT(7)
> +#define UNICAM_OES BIT(8)
> +#define UNICAM_IFO BIT(9)
> +#define UNICAM_OFO BIT(10)
> +#define UNICAM_BFO BIT(11)
> +#define UNICAM_DL BIT(12)
> +#define UNICAM_PS BIT(13)
> +#define UNICAM_IS BIT(14)
> +#define UNICAM_PI0 BIT(15)
> +#define UNICAM_PI1 BIT(16)
> +#define UNICAM_FSI_S BIT(17)
> +#define UNICAM_FEI_S BIT(18)
> +#define UNICAM_LCI_S BIT(19)
> +#define UNICAM_BUF0_RDY BIT(20)
> +#define UNICAM_BUF0_NO BIT(21)
> +#define UNICAM_BUF1_RDY BIT(22)
> +#define UNICAM_BUF1_NO BIT(23)
> +#define UNICAM_DI BIT(24)
> +
> +#define UNICAM_STA_MASK_ALL \
> + (UNICAM_DL + \
> + UNICAM_SBE + \
> + UNICAM_PBE + \
> + UNICAM_HOE + \
> + UNICAM_PLE + \
> + UNICAM_SSC + \
> + UNICAM_CRCE + \
> + UNICAM_IFO + \
> + UNICAM_OFO + \
> + UNICAM_PS + \
> + UNICAM_PI0 + \
> + UNICAM_PI1)
> +
> +/* UNICAM_ANA Register */
> +#define UNICAM_APD BIT(0)
> +#define UNICAM_BPD BIT(1)
> +#define UNICAM_AR BIT(2)
> +#define UNICAM_DDL BIT(3)
> +#define UNICAM_CTATADJ_MASK GENMASK(7, 4)
> +#define UNICAM_PTATADJ_MASK GENMASK(11, 8)
> +
> +/* UNICAM_PRI Register */
> +#define UNICAM_PE BIT(0)
> +#define UNICAM_PT_MASK GENMASK(2, 1)
> +#define UNICAM_NP_MASK GENMASK(7, 4)
> +#define UNICAM_PP_MASK GENMASK(11, 8)
> +#define UNICAM_BS_MASK GENMASK(15, 12)
> +#define UNICAM_BL_MASK GENMASK(17, 16)
> +
> +/* UNICAM_CLK Register */
> +#define UNICAM_CLE BIT(0)
> +#define UNICAM_CLPD BIT(1)
> +#define UNICAM_CLLPE BIT(2)
> +#define UNICAM_CLHSE BIT(3)
> +#define UNICAM_CLTRE BIT(4)
> +#define UNICAM_CLAC_MASK GENMASK(8, 5)
> +#define UNICAM_CLSTE BIT(29)
> +
> +/* UNICAM_CLT Register */
> +#define UNICAM_CLT1_MASK GENMASK(7, 0)
> +#define UNICAM_CLT2_MASK GENMASK(15, 8)
> +
> +/* UNICAM_DATn Registers */
> +#define UNICAM_DLE BIT(0)
> +#define UNICAM_DLPD BIT(1)
> +#define UNICAM_DLLPE BIT(2)
> +#define UNICAM_DLHSE BIT(3)
> +#define UNICAM_DLTRE BIT(4)
> +#define UNICAM_DLSM BIT(5)
> +#define UNICAM_DLFO BIT(28)
> +#define UNICAM_DLSTE BIT(29)
> +
> +#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO)
> +
> +/* UNICAM_DLT Register */
> +#define UNICAM_DLT1_MASK GENMASK(7, 0)
> +#define UNICAM_DLT2_MASK GENMASK(15, 8)
> +#define UNICAM_DLT3_MASK GENMASK(23, 16)
> +
> +/* UNICAM_ICTL Register */
> +#define UNICAM_FSIE BIT(0)
> +#define UNICAM_FEIE BIT(1)
> +#define UNICAM_IBOB BIT(2)
> +#define UNICAM_FCM BIT(3)
> +#define UNICAM_TFC BIT(4)
> +#define UNICAM_LIP_MASK GENMASK(6, 5)
> +#define UNICAM_LCIE_MASK GENMASK(28, 16)
> +
> +/* UNICAM_IDI0/1 Register */
> +#define UNICAM_ID0_MASK GENMASK(7, 0)
> +#define UNICAM_ID1_MASK GENMASK(15, 8)
> +#define UNICAM_ID2_MASK GENMASK(23, 16)
> +#define UNICAM_ID3_MASK GENMASK(31, 24)
> +
> +/* UNICAM_ISTA Register */
> +#define UNICAM_FSI BIT(0)
> +#define UNICAM_FEI BIT(1)
> +#define UNICAM_LCI BIT(2)
> +
> +#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI)
> +
> +/* UNICAM_IPIPE Register */
> +#define UNICAM_PUM_MASK GENMASK(2, 0)
> + /* Unpacking modes */
> + #define UNICAM_PUM_NONE 0
> + #define UNICAM_PUM_UNPACK6 1
> + #define UNICAM_PUM_UNPACK7 2
> + #define UNICAM_PUM_UNPACK8 3
> + #define UNICAM_PUM_UNPACK10 4
> + #define UNICAM_PUM_UNPACK12 5
> + #define UNICAM_PUM_UNPACK14 6
> + #define UNICAM_PUM_UNPACK16 7
> +#define UNICAM_DDM_MASK GENMASK(6, 3)
> +#define UNICAM_PPM_MASK GENMASK(9, 7)
> + /* Packing modes */
> + #define UNICAM_PPM_NONE 0
> + #define UNICAM_PPM_PACK8 1
> + #define UNICAM_PPM_PACK10 2
> + #define UNICAM_PPM_PACK12 3
> + #define UNICAM_PPM_PACK14 4
> + #define UNICAM_PPM_PACK16 5
> +#define UNICAM_DEM_MASK GENMASK(11, 10)
> +#define UNICAM_DEBL_MASK GENMASK(14, 12)
> +#define UNICAM_ICM_MASK GENMASK(16, 15)
> +#define UNICAM_IDM_MASK GENMASK(17, 17)
> +
> +/* UNICAM_ICC Register */
> +#define UNICAM_ICFL_MASK GENMASK(4, 0)
> +#define UNICAM_ICFH_MASK GENMASK(9, 5)
> +#define UNICAM_ICST_MASK GENMASK(12, 10)
> +#define UNICAM_ICLT_MASK GENMASK(15, 13)
> +#define UNICAM_ICLL_MASK GENMASK(31, 16)
> +
> +/* UNICAM_DCS Register */
> +#define UNICAM_DIE BIT(0)
> +#define UNICAM_DIM BIT(1)
> +#define UNICAM_DBOB BIT(3)
> +#define UNICAM_FDE BIT(4)
> +#define UNICAM_LDP BIT(5)
> +#define UNICAM_EDL_MASK GENMASK(15, 8)
> +
> +/* UNICAM_DBCTL Register */
> +#define UNICAM_DBEN BIT(0)
> +#define UNICAM_BUF0_IE BIT(1)
> +#define UNICAM_BUF1_IE BIT(2)
> +
> +/* UNICAM_CMP[0,1] register */
> +#define UNICAM_PCE BIT(31)
> +#define UNICAM_GI BIT(9)
> +#define UNICAM_CPH BIT(8)
> +#define UNICAM_PCVC_MASK GENMASK(7, 6)
> +#define UNICAM_PCDT_MASK GENMASK(5, 0)
> +
> +/* UNICAM_MISC register */
> +#define UNICAM_FL0 BIT(6)
> +#define UNICAM_FL1 BIT(9)
> +
> +#endif
>
Regards,
Hans
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-09-22 6:45 ` Stefan Wahren
@ 2017-09-22 16:07 ` Dave Stevenson
-1 siblings, 0 replies; 30+ messages in thread
From: Dave Stevenson @ 2017-09-22 16:07 UTC (permalink / raw)
To: Stefan Wahren
Cc: Mauro Carvalho Chehab, Eric Anholt,
linux-media-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Sakari Ailus,
Hans Verkuil, linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
Hi Stefan
On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org> wrote:
> Hi Dave,
>
>> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> hat am 20. September 2017 um 18:07 geschrieben:
>>
>>
>> Document the DT bindings for the CSI2/CCP2 receiver peripheral
>> (known as Unicam) on BCM283x SoCs.
>>
>> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
>> ---
>>
>> Changes since v2
>> - Removed all references to Linux drivers.
>> - Reworded section about disabling the firmware driver.
>> - Renamed clock from "lp_clock" to "lp" in description and example.
>> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>> and data-lanes.
>> - Corrected typo in example from csi to csi1.
>> - Removed unnecessary #address-cells and #size-cells in example.
>> - Removed setting of status from the example.
>>
>> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>> 1 file changed, 85 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..7714fb3
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> @@ -0,0 +1,85 @@
>> +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.
>> +
>> +The main platform using this SoC is the Raspberry Pi family of boards.
>> +On the Pi the VideoCore firmware can also control this hardware block,
>> +and driving it from two different processors will cause issues.
>> +To avoid this, the firmware checks the device tree configuration
>> +during boot. If it finds device tree nodes called csi0 or csi1 then
>> +it will stop the firmware accessing the block, and it can then
>> +safely be used via the device tree binding.
>> +
>> +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" entry, matching entries in the
>> + clocks property.
>> +
>> +Unicam supports a single port node. It should contain one 'port' child node
>> +with child 'endpoint' node. Please refer to the bindings defined in
>> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>> +
>> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>> +are mandatory.
>> +Data lane reordering is not supported so the data lanes must be in order,
>> +starting at 1. The number of data lanes should represent the number of
>> +usable lanes for the hardware block. That may be limited by either the SoC or
>> +how the platform presents the interface, and the lower value must be used.
>> +
>> +Lane reordering is not supported on the clock lane either, so the optional
>> +property "clock-lane" will implicitly be <0>.
>> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>> +implicitly be <0 0 0 0 0>.
>> +Neither of these values will be checked.
>> +
>> +Example:
>> + csi1: csi1@7e801000 {
>> + compatible = "brcm,bcm2835-unicam";
>> + reg = <0x7e801000 0x800>,
>> + <0x7e802004 0x4>;
>
> sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
CMI (Clock Manager Image) consists of a total of 4 registers.
0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
inversion of the clock and data lanes (2 data lanes available on
CAM0).
0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
inversion of the clock and data lanes (4 data lanes available on
CAM1).
0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
default value is the required value. Nothing touches it that I can
find.
The range listed only covers the one register associated with that
Unicam instance, so no other users. The other two aren't touched.
Do 16 active register bits solely for camera clock gating really
warrant a full clock driver?
Dave
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-09-22 16:07 ` Dave Stevenson
0 siblings, 0 replies; 30+ messages in thread
From: Dave Stevenson @ 2017-09-22 16:07 UTC (permalink / raw)
To: Stefan Wahren
Cc: Mauro Carvalho Chehab, Eric Anholt, linux-media, Rob Herring,
Sakari Ailus, Hans Verkuil, linux-rpi-kernel, devicetree
Hi Stefan
On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
> Hi Dave,
>
>> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>>
>>
>> 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>
>> ---
>>
>> Changes since v2
>> - Removed all references to Linux drivers.
>> - Reworded section about disabling the firmware driver.
>> - Renamed clock from "lp_clock" to "lp" in description and example.
>> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>> and data-lanes.
>> - Corrected typo in example from csi to csi1.
>> - Removed unnecessary #address-cells and #size-cells in example.
>> - Removed setting of status from the example.
>>
>> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>> 1 file changed, 85 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..7714fb3
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> @@ -0,0 +1,85 @@
>> +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.
>> +
>> +The main platform using this SoC is the Raspberry Pi family of boards.
>> +On the Pi the VideoCore firmware can also control this hardware block,
>> +and driving it from two different processors will cause issues.
>> +To avoid this, the firmware checks the device tree configuration
>> +during boot. If it finds device tree nodes called csi0 or csi1 then
>> +it will stop the firmware accessing the block, and it can then
>> +safely be used via the device tree binding.
>> +
>> +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" entry, matching entries in the
>> + clocks property.
>> +
>> +Unicam supports a single port node. It should contain one 'port' child node
>> +with child 'endpoint' node. Please refer to the bindings defined in
>> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>> +
>> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>> +are mandatory.
>> +Data lane reordering is not supported so the data lanes must be in order,
>> +starting at 1. The number of data lanes should represent the number of
>> +usable lanes for the hardware block. That may be limited by either the SoC or
>> +how the platform presents the interface, and the lower value must be used.
>> +
>> +Lane reordering is not supported on the clock lane either, so the optional
>> +property "clock-lane" will implicitly be <0>.
>> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>> +implicitly be <0 0 0 0 0>.
>> +Neither of these values will be checked.
>> +
>> +Example:
>> + csi1: csi1@7e801000 {
>> + compatible = "brcm,bcm2835-unicam";
>> + reg = <0x7e801000 0x800>,
>> + <0x7e802004 0x4>;
>
> sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
CMI (Clock Manager Image) consists of a total of 4 registers.
0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
inversion of the clock and data lanes (2 data lanes available on
CAM0).
0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
inversion of the clock and data lanes (4 data lanes available on
CAM1).
0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
default value is the required value. Nothing touches it that I can
find.
The range listed only covers the one register associated with that
Unicam instance, so no other users. The other two aren't touched.
Do 16 active register bits solely for camera clock gating really
warrant a full clock driver?
Dave
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-09-22 16:07 ` Dave Stevenson
@ 2017-09-22 17:39 ` Stefan Wahren
-1 siblings, 0 replies; 30+ messages in thread
From: Stefan Wahren @ 2017-09-22 17:39 UTC (permalink / raw)
To: Dave Stevenson
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Sakari Ailus,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Hans Verkuil,
Mauro Carvalho Chehab, linux-media-u79uwXL29TY76Z2rM5mHXA
Hi Dave,
> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> hat am 22. September 2017 um 18:07 geschrieben:
>
>
> Hi Stefan
>
> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org> wrote:
> > Hi Dave,
> >
> >> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> hat am 20. September 2017 um 18:07 geschrieben:
> >>
> >>
> >> Document the DT bindings for the CSI2/CCP2 receiver peripheral
> >> (known as Unicam) on BCM283x SoCs.
> >>
> >> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
> >> ---
> >>
> >> Changes since v2
> >> - Removed all references to Linux drivers.
> >> - Reworded section about disabling the firmware driver.
> >> - Renamed clock from "lp_clock" to "lp" in description and example.
> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
> >> and data-lanes.
> >> - Corrected typo in example from csi to csi1.
> >> - Removed unnecessary #address-cells and #size-cells in example.
> >> - Removed setting of status from the example.
> >>
> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
> >> 1 file changed, 85 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..7714fb3
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> >> @@ -0,0 +1,85 @@
> >> +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.
> >> +
> >> +The main platform using this SoC is the Raspberry Pi family of boards.
> >> +On the Pi the VideoCore firmware can also control this hardware block,
> >> +and driving it from two different processors will cause issues.
> >> +To avoid this, the firmware checks the device tree configuration
> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
> >> +it will stop the firmware accessing the block, and it can then
> >> +safely be used via the device tree binding.
> >> +
> >> +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" entry, matching entries in the
> >> + clocks property.
> >> +
> >> +Unicam supports a single port node. It should contain one 'port' child node
> >> +with child 'endpoint' node. Please refer to the bindings defined in
> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> >> +
> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
> >> +are mandatory.
> >> +Data lane reordering is not supported so the data lanes must be in order,
> >> +starting at 1. The number of data lanes should represent the number of
> >> +usable lanes for the hardware block. That may be limited by either the SoC or
> >> +how the platform presents the interface, and the lower value must be used.
> >> +
> >> +Lane reordering is not supported on the clock lane either, so the optional
> >> +property "clock-lane" will implicitly be <0>.
> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> >> +implicitly be <0 0 0 0 0>.
> >> +Neither of these values will be checked.
> >> +
> >> +Example:
> >> + csi1: csi1@7e801000 {
> >> + compatible = "brcm,bcm2835-unicam";
> >> + reg = <0x7e801000 0x800>,
> >> + <0x7e802004 0x4>;
> >
> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>
> CMI (Clock Manager Image) consists of a total of 4 registers.
> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
> inversion of the clock and data lanes (2 data lanes available on
> CAM0).
> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
> inversion of the clock and data lanes (4 data lanes available on
> CAM1).
> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
> default value is the required value. Nothing touches it that I can
> find.
thanks for providing these information.
>
> The range listed only covers the one register associated with that
> Unicam instance, so no other users. The other two aren't touched.
> Do 16 active register bits solely for camera clock gating really
> warrant a full clock driver?
As long as there no new driver in the future, which also needs to touch these gates i'm fine.
Stefan
>
> Dave
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-09-22 17:39 ` Stefan Wahren
0 siblings, 0 replies; 30+ messages in thread
From: Stefan Wahren @ 2017-09-22 17:39 UTC (permalink / raw)
To: Dave Stevenson
Cc: Mauro Carvalho Chehab, Eric Anholt, linux-media, Rob Herring,
Sakari Ailus, Hans Verkuil, linux-rpi-kernel, devicetree
Hi Dave,
> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 22. September 2017 um 18:07 geschrieben:
>
>
> Hi Stefan
>
> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
> > Hi Dave,
> >
> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
> >>
> >>
> >> 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>
> >> ---
> >>
> >> Changes since v2
> >> - Removed all references to Linux drivers.
> >> - Reworded section about disabling the firmware driver.
> >> - Renamed clock from "lp_clock" to "lp" in description and example.
> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
> >> and data-lanes.
> >> - Corrected typo in example from csi to csi1.
> >> - Removed unnecessary #address-cells and #size-cells in example.
> >> - Removed setting of status from the example.
> >>
> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
> >> 1 file changed, 85 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..7714fb3
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> >> @@ -0,0 +1,85 @@
> >> +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.
> >> +
> >> +The main platform using this SoC is the Raspberry Pi family of boards.
> >> +On the Pi the VideoCore firmware can also control this hardware block,
> >> +and driving it from two different processors will cause issues.
> >> +To avoid this, the firmware checks the device tree configuration
> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
> >> +it will stop the firmware accessing the block, and it can then
> >> +safely be used via the device tree binding.
> >> +
> >> +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" entry, matching entries in the
> >> + clocks property.
> >> +
> >> +Unicam supports a single port node. It should contain one 'port' child node
> >> +with child 'endpoint' node. Please refer to the bindings defined in
> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> >> +
> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
> >> +are mandatory.
> >> +Data lane reordering is not supported so the data lanes must be in order,
> >> +starting at 1. The number of data lanes should represent the number of
> >> +usable lanes for the hardware block. That may be limited by either the SoC or
> >> +how the platform presents the interface, and the lower value must be used.
> >> +
> >> +Lane reordering is not supported on the clock lane either, so the optional
> >> +property "clock-lane" will implicitly be <0>.
> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> >> +implicitly be <0 0 0 0 0>.
> >> +Neither of these values will be checked.
> >> +
> >> +Example:
> >> + csi1: csi1@7e801000 {
> >> + compatible = "brcm,bcm2835-unicam";
> >> + reg = <0x7e801000 0x800>,
> >> + <0x7e802004 0x4>;
> >
> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>
> CMI (Clock Manager Image) consists of a total of 4 registers.
> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
> inversion of the clock and data lanes (2 data lanes available on
> CAM0).
> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
> inversion of the clock and data lanes (4 data lanes available on
> CAM1).
> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
> default value is the required value. Nothing touches it that I can
> find.
thanks for providing these information.
>
> The range listed only covers the one register associated with that
> Unicam instance, so no other users. The other two aren't touched.
> Do 16 active register bits solely for camera clock gating really
> warrant a full clock driver?
As long as there no new driver in the future, which also needs to touch these gates i'm fine.
Stefan
>
> Dave
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-09-22 16:07 ` Dave Stevenson
@ 2017-09-22 18:04 ` Eric Anholt
-1 siblings, 0 replies; 30+ messages in thread
From: Eric Anholt @ 2017-09-22 18:04 UTC (permalink / raw)
To: Dave Stevenson, Stefan Wahren
Cc: Mauro Carvalho Chehab, linux-media-u79uwXL29TY76Z2rM5mHXA,
Rob Herring, Sakari Ailus, Hans Verkuil,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
[-- Attachment #1: Type: text/plain, Size: 4861 bytes --]
Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> writes:
> Hi Stefan
>
> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org> wrote:
>> Hi Dave,
>>
>>> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> hat am 20. September 2017 um 18:07 geschrieben:
>>>
>>>
>>> Document the DT bindings for the CSI2/CCP2 receiver peripheral
>>> (known as Unicam) on BCM283x SoCs.
>>>
>>> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
>>> ---
>>>
>>> Changes since v2
>>> - Removed all references to Linux drivers.
>>> - Reworded section about disabling the firmware driver.
>>> - Renamed clock from "lp_clock" to "lp" in description and example.
>>> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>> and data-lanes.
>>> - Corrected typo in example from csi to csi1.
>>> - Removed unnecessary #address-cells and #size-cells in example.
>>> - Removed setting of status from the example.
>>>
>>> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>> 1 file changed, 85 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..7714fb3
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>> @@ -0,0 +1,85 @@
>>> +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.
>>> +
>>> +The main platform using this SoC is the Raspberry Pi family of boards.
>>> +On the Pi the VideoCore firmware can also control this hardware block,
>>> +and driving it from two different processors will cause issues.
>>> +To avoid this, the firmware checks the device tree configuration
>>> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>> +it will stop the firmware accessing the block, and it can then
>>> +safely be used via the device tree binding.
>>> +
>>> +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" entry, matching entries in the
>>> + clocks property.
>>> +
>>> +Unicam supports a single port node. It should contain one 'port' child node
>>> +with child 'endpoint' node. Please refer to the bindings defined in
>>> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>> +
>>> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>> +are mandatory.
>>> +Data lane reordering is not supported so the data lanes must be in order,
>>> +starting at 1. The number of data lanes should represent the number of
>>> +usable lanes for the hardware block. That may be limited by either the SoC or
>>> +how the platform presents the interface, and the lower value must be used.
>>> +
>>> +Lane reordering is not supported on the clock lane either, so the optional
>>> +property "clock-lane" will implicitly be <0>.
>>> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>> +implicitly be <0 0 0 0 0>.
>>> +Neither of these values will be checked.
>>> +
>>> +Example:
>>> + csi1: csi1@7e801000 {
>>> + compatible = "brcm,bcm2835-unicam";
>>> + reg = <0x7e801000 0x800>,
>>> + <0x7e802004 0x4>;
>>
>> sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>
> CMI (Clock Manager Image) consists of a total of 4 registers.
> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
> inversion of the clock and data lanes (2 data lanes available on
> CAM0).
> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
> inversion of the clock and data lanes (4 data lanes available on
> CAM1).
> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
> default value is the required value. Nothing touches it that I can
> find.
Yeah, my conclusion previously was that it's appropriate to consider
this register as part of the unicam instance. No other HW block could
want to talk to it.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-09-22 18:04 ` Eric Anholt
0 siblings, 0 replies; 30+ messages in thread
From: Eric Anholt @ 2017-09-22 18:04 UTC (permalink / raw)
To: Dave Stevenson, Stefan Wahren
Cc: Mauro Carvalho Chehab, linux-media, Rob Herring, Sakari Ailus,
Hans Verkuil, linux-rpi-kernel, devicetree
[-- Attachment #1: Type: text/plain, Size: 4769 bytes --]
Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
> Hi Stefan
>
> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>> Hi Dave,
>>
>>> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>>>
>>>
>>> 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>
>>> ---
>>>
>>> Changes since v2
>>> - Removed all references to Linux drivers.
>>> - Reworded section about disabling the firmware driver.
>>> - Renamed clock from "lp_clock" to "lp" in description and example.
>>> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>> and data-lanes.
>>> - Corrected typo in example from csi to csi1.
>>> - Removed unnecessary #address-cells and #size-cells in example.
>>> - Removed setting of status from the example.
>>>
>>> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>> 1 file changed, 85 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..7714fb3
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>> @@ -0,0 +1,85 @@
>>> +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.
>>> +
>>> +The main platform using this SoC is the Raspberry Pi family of boards.
>>> +On the Pi the VideoCore firmware can also control this hardware block,
>>> +and driving it from two different processors will cause issues.
>>> +To avoid this, the firmware checks the device tree configuration
>>> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>> +it will stop the firmware accessing the block, and it can then
>>> +safely be used via the device tree binding.
>>> +
>>> +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" entry, matching entries in the
>>> + clocks property.
>>> +
>>> +Unicam supports a single port node. It should contain one 'port' child node
>>> +with child 'endpoint' node. Please refer to the bindings defined in
>>> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>> +
>>> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>> +are mandatory.
>>> +Data lane reordering is not supported so the data lanes must be in order,
>>> +starting at 1. The number of data lanes should represent the number of
>>> +usable lanes for the hardware block. That may be limited by either the SoC or
>>> +how the platform presents the interface, and the lower value must be used.
>>> +
>>> +Lane reordering is not supported on the clock lane either, so the optional
>>> +property "clock-lane" will implicitly be <0>.
>>> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>> +implicitly be <0 0 0 0 0>.
>>> +Neither of these values will be checked.
>>> +
>>> +Example:
>>> + csi1: csi1@7e801000 {
>>> + compatible = "brcm,bcm2835-unicam";
>>> + reg = <0x7e801000 0x800>,
>>> + <0x7e802004 0x4>;
>>
>> sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>
> CMI (Clock Manager Image) consists of a total of 4 registers.
> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
> inversion of the clock and data lanes (2 data lanes available on
> CAM0).
> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
> inversion of the clock and data lanes (4 data lanes available on
> CAM1).
> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
> default value is the required value. Nothing touches it that I can
> find.
Yeah, my conclusion previously was that it's appropriate to consider
this register as part of the unicam instance. No other HW block could
want to talk to it.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-09-22 16:07 ` Dave Stevenson
@ 2017-09-27 21:51 ` Rob Herring
-1 siblings, 0 replies; 30+ messages in thread
From: Rob Herring @ 2017-09-27 21:51 UTC (permalink / raw)
To: Dave Stevenson
Cc: Stefan Wahren, Mauro Carvalho Chehab, Eric Anholt,
linux-media-u79uwXL29TY76Z2rM5mHXA, Sakari Ailus, Hans Verkuil,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
> Hi Stefan
>
> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org> wrote:
> > Hi Dave,
> >
> >> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> hat am 20. September 2017 um 18:07 geschrieben:
> >>
> >>
> >> Document the DT bindings for the CSI2/CCP2 receiver peripheral
> >> (known as Unicam) on BCM283x SoCs.
> >>
> >> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
> >> ---
> >>
> >> Changes since v2
> >> - Removed all references to Linux drivers.
> >> - Reworded section about disabling the firmware driver.
> >> - Renamed clock from "lp_clock" to "lp" in description and example.
> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
> >> and data-lanes.
> >> - Corrected typo in example from csi to csi1.
> >> - Removed unnecessary #address-cells and #size-cells in example.
> >> - Removed setting of status from the example.
> >>
> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
> >> 1 file changed, 85 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..7714fb3
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> >> @@ -0,0 +1,85 @@
> >> +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.
> >> +
> >> +The main platform using this SoC is the Raspberry Pi family of boards.
> >> +On the Pi the VideoCore firmware can also control this hardware block,
> >> +and driving it from two different processors will cause issues.
> >> +To avoid this, the firmware checks the device tree configuration
> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
> >> +it will stop the firmware accessing the block, and it can then
> >> +safely be used via the device tree binding.
> >> +
> >> +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" entry, matching entries in the
> >> + clocks property.
> >> +
> >> +Unicam supports a single port node. It should contain one 'port' child node
> >> +with child 'endpoint' node. Please refer to the bindings defined in
> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> >> +
> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
> >> +are mandatory.
> >> +Data lane reordering is not supported so the data lanes must be in order,
> >> +starting at 1. The number of data lanes should represent the number of
> >> +usable lanes for the hardware block. That may be limited by either the SoC or
> >> +how the platform presents the interface, and the lower value must be used.
> >> +
> >> +Lane reordering is not supported on the clock lane either, so the optional
> >> +property "clock-lane" will implicitly be <0>.
> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> >> +implicitly be <0 0 0 0 0>.
> >> +Neither of these values will be checked.
> >> +
> >> +Example:
> >> + csi1: csi1@7e801000 {
> >> + compatible = "brcm,bcm2835-unicam";
> >> + reg = <0x7e801000 0x800>,
> >> + <0x7e802004 0x4>;
> >
> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>
> CMI (Clock Manager Image) consists of a total of 4 registers.
> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
> inversion of the clock and data lanes (2 data lanes available on
> CAM0).
> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
> inversion of the clock and data lanes (4 data lanes available on
> CAM1).
> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
> default value is the required value. Nothing touches it that I can
> find.
>
> The range listed only covers the one register associated with that
> Unicam instance, so no other users. The other two aren't touched.
> Do 16 active register bits solely for camera clock gating really
> warrant a full clock driver?
You should describe all the registers in DT, not just what the driver
(currently) uses.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-09-27 21:51 ` Rob Herring
0 siblings, 0 replies; 30+ messages in thread
From: Rob Herring @ 2017-09-27 21:51 UTC (permalink / raw)
To: Dave Stevenson
Cc: Stefan Wahren, Mauro Carvalho Chehab, Eric Anholt, linux-media,
Sakari Ailus, Hans Verkuil, linux-rpi-kernel, devicetree
On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
> Hi Stefan
>
> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
> > Hi Dave,
> >
> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
> >>
> >>
> >> 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>
> >> ---
> >>
> >> Changes since v2
> >> - Removed all references to Linux drivers.
> >> - Reworded section about disabling the firmware driver.
> >> - Renamed clock from "lp_clock" to "lp" in description and example.
> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
> >> and data-lanes.
> >> - Corrected typo in example from csi to csi1.
> >> - Removed unnecessary #address-cells and #size-cells in example.
> >> - Removed setting of status from the example.
> >>
> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
> >> 1 file changed, 85 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..7714fb3
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> >> @@ -0,0 +1,85 @@
> >> +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.
> >> +
> >> +The main platform using this SoC is the Raspberry Pi family of boards.
> >> +On the Pi the VideoCore firmware can also control this hardware block,
> >> +and driving it from two different processors will cause issues.
> >> +To avoid this, the firmware checks the device tree configuration
> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
> >> +it will stop the firmware accessing the block, and it can then
> >> +safely be used via the device tree binding.
> >> +
> >> +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" entry, matching entries in the
> >> + clocks property.
> >> +
> >> +Unicam supports a single port node. It should contain one 'port' child node
> >> +with child 'endpoint' node. Please refer to the bindings defined in
> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> >> +
> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
> >> +are mandatory.
> >> +Data lane reordering is not supported so the data lanes must be in order,
> >> +starting at 1. The number of data lanes should represent the number of
> >> +usable lanes for the hardware block. That may be limited by either the SoC or
> >> +how the platform presents the interface, and the lower value must be used.
> >> +
> >> +Lane reordering is not supported on the clock lane either, so the optional
> >> +property "clock-lane" will implicitly be <0>.
> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> >> +implicitly be <0 0 0 0 0>.
> >> +Neither of these values will be checked.
> >> +
> >> +Example:
> >> + csi1: csi1@7e801000 {
> >> + compatible = "brcm,bcm2835-unicam";
> >> + reg = <0x7e801000 0x800>,
> >> + <0x7e802004 0x4>;
> >
> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>
> CMI (Clock Manager Image) consists of a total of 4 registers.
> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
> inversion of the clock and data lanes (2 data lanes available on
> CAM0).
> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
> inversion of the clock and data lanes (4 data lanes available on
> CAM1).
> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
> default value is the required value. Nothing touches it that I can
> find.
>
> The range listed only covers the one register associated with that
> Unicam instance, so no other users. The other two aren't touched.
> Do 16 active register bits solely for camera clock gating really
> warrant a full clock driver?
You should describe all the registers in DT, not just what the driver
(currently) uses.
Rob
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-09-27 21:51 ` Rob Herring
(?)
@ 2017-10-02 10:36 ` Dave Stevenson
[not found] ` <CAAoAYcM0m6Z8hUDn+FuNb-O28geAYJqHWrhKPDP_Jvh2P-YE3A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
-1 siblings, 1 reply; 30+ messages in thread
From: Dave Stevenson @ 2017-10-02 10:36 UTC (permalink / raw)
To: Rob Herring
Cc: Stefan Wahren, Mauro Carvalho Chehab, Eric Anholt, linux-media,
Sakari Ailus, Hans Verkuil, linux-rpi-kernel, devicetree
Hi Rob
On 27 September 2017 at 22:51, Rob Herring <robh@kernel.org> wrote:
> On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
>> Hi Stefan
>>
>> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>> > Hi Dave,
>> >
>> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>> >>
>> >>
>> >> 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>
>> >> ---
>> >>
>> >> Changes since v2
>> >> - Removed all references to Linux drivers.
>> >> - Reworded section about disabling the firmware driver.
>> >> - Renamed clock from "lp_clock" to "lp" in description and example.
>> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>> >> and data-lanes.
>> >> - Corrected typo in example from csi to csi1.
>> >> - Removed unnecessary #address-cells and #size-cells in example.
>> >> - Removed setting of status from the example.
>> >>
>> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>> >> 1 file changed, 85 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..7714fb3
>> >> --- /dev/null
>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> >> @@ -0,0 +1,85 @@
>> >> +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.
>> >> +
>> >> +The main platform using this SoC is the Raspberry Pi family of boards.
>> >> +On the Pi the VideoCore firmware can also control this hardware block,
>> >> +and driving it from two different processors will cause issues.
>> >> +To avoid this, the firmware checks the device tree configuration
>> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
>> >> +it will stop the firmware accessing the block, and it can then
>> >> +safely be used via the device tree binding.
>> >> +
>> >> +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" entry, matching entries in the
>> >> + clocks property.
>> >> +
>> >> +Unicam supports a single port node. It should contain one 'port' child node
>> >> +with child 'endpoint' node. Please refer to the bindings defined in
>> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>> >> +
>> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>> >> +are mandatory.
>> >> +Data lane reordering is not supported so the data lanes must be in order,
>> >> +starting at 1. The number of data lanes should represent the number of
>> >> +usable lanes for the hardware block. That may be limited by either the SoC or
>> >> +how the platform presents the interface, and the lower value must be used.
>> >> +
>> >> +Lane reordering is not supported on the clock lane either, so the optional
>> >> +property "clock-lane" will implicitly be <0>.
>> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>> >> +implicitly be <0 0 0 0 0>.
>> >> +Neither of these values will be checked.
>> >> +
>> >> +Example:
>> >> + csi1: csi1@7e801000 {
>> >> + compatible = "brcm,bcm2835-unicam";
>> >> + reg = <0x7e801000 0x800>,
>> >> + <0x7e802004 0x4>;
>> >
>> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>>
>> CMI (Clock Manager Image) consists of a total of 4 registers.
>> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
>> inversion of the clock and data lanes (2 data lanes available on
>> CAM0).
>> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
>> inversion of the clock and data lanes (4 data lanes available on
>> CAM1).
>> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
>> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
>> default value is the required value. Nothing touches it that I can
>> find.
>>
>> The range listed only covers the one register associated with that
>> Unicam instance, so no other users. The other two aren't touched.
>> Do 16 active register bits solely for camera clock gating really
>> warrant a full clock driver?
>
> You should describe all the registers in DT, not just what the driver
> (currently) uses.
I'm not clear what you're asking for here.
This binding is for the Unicam block, not for CMI (Clock Manager
Imaging). In order for a Unicam instance to work, it needs to enable
the relevant clock gating via 1 CMI register, and it will only ever be
one register.
Eric and Stefan as the platform maintainers have already both
acknowledged that a full clock driver for the CMI block is not
warranted - the only user would be this driver, and the other
registers in the CMI block are not useful.
Do you want a random text description of a different block within this
binding? Or are you requesting a full clock driver for the CMI block?
Or that all Unicam instances should be mapping the whole 4 register
range of CMI, and then somehow working out which register within that
block they should be mapping?
Regards,
Dave
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
2017-09-20 16:07 ` Dave Stevenson
(?)
@ 2017-10-13 21:09 ` Sakari Ailus
2017-10-17 9:17 ` Dave Stevenson
-1 siblings, 1 reply; 30+ messages in thread
From: Sakari Ailus @ 2017-10-13 21:09 UTC (permalink / raw)
To: Dave Stevenson
Cc: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
Stefan Wahren, linux-media, linux-rpi-kernel, devicetree
Hi Dave,
On Wed, Sep 20, 2017 at 05:07:54PM +0100, Dave Stevenson wrote:
> New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
> that converts a fourcc into a nice printable version.
>
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
> ---
>
> No changes from v2 to v3
>
> drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
> include/media/v4l2-common.h | 3 +++
> 2 files changed, 21 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> index a5ea1f5..0219895 100644
> --- a/drivers/media/v4l2-core/v4l2-common.c
> +++ b/drivers/media/v4l2-core/v4l2-common.c
> @@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
> tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
> }
> EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
> +
> +char *v4l2_fourcc2s(u32 fourcc, char *buf)
> +{
> + buf[0] = fourcc & 0x7f;
> + buf[1] = (fourcc >> 8) & 0x7f;
> + buf[2] = (fourcc >> 16) & 0x7f;
> + buf[3] = (fourcc >> 24) & 0x7f;
> + if (fourcc & (1 << 31)) {
> + buf[4] = '-';
> + buf[5] = 'B';
> + buf[6] = 'E';
> + buf[7] = '\0';
> + } else {
> + buf[4] = '\0';
> + }
> + return buf;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
> index aac8b7b..5b0fff9 100644
> --- a/include/media/v4l2-common.h
> +++ b/include/media/v4l2-common.h
> @@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
>
> void v4l2_get_timestamp(struct timeval *tv);
>
> +#define V4L2_FOURCC_MAX_SIZE 8
> +char *v4l2_fourcc2s(u32 fourcc, char *buf);
> +
> #endif /* V4L2_COMMON_H_ */
I like the idea but the use of a character pointer and assuming a length
looks a bit scary.
As this seems to be used uniquely for printing stuff, a couple of macros
could be used instead. Something like
#define V4L2_FOURCC_CONV "%c%c%c%c%s"
#define V4L2_FOURCC_TO_CONV(fourcc) \
fourcc & 0x7f, (fourcc >> 8) & 0x7f, (fourcc >> 16) & 0x7f, \
(fourcc >> 24) & 0x7f, fourcc & BIT(31) ? "-BE" : ""
You could use it with printk-style functions, e.g.
printk("blah fourcc " V4L2_FOURCC_CONV " is a nice format",
V4L2_FOURCC_TO_CONV(fourcc));
I guess it'd be better to add more parentheses around "fourcc" but it'd be
less clear here that way.
--
Regards,
Sakari Ailus
e-mail: sakari.ailus@iki.fi
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
2017-10-13 21:09 ` Sakari Ailus
@ 2017-10-17 9:17 ` Dave Stevenson
[not found] ` <CAAoAYcN441pVUqCu00hbKmEQWyNaK4jdwkufpJ2P8iXkcQG5KA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
0 siblings, 1 reply; 30+ messages in thread
From: Dave Stevenson @ 2017-10-17 9:17 UTC (permalink / raw)
To: Sakari Ailus, Hans Verkuil
Cc: Mauro Carvalho Chehab, Rob Herring, Eric Anholt, Stefan Wahren,
linux-media, linux-rpi-kernel, devicetree
Hi Sakari.
On 13 October 2017 at 22:09, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Dave,
>
> On Wed, Sep 20, 2017 at 05:07:54PM +0100, Dave Stevenson wrote:
>> New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
>> that converts a fourcc into a nice printable version.
>>
>> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
>> ---
>>
>> No changes from v2 to v3
>>
>> drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
>> include/media/v4l2-common.h | 3 +++
>> 2 files changed, 21 insertions(+)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
>> index a5ea1f5..0219895 100644
>> --- a/drivers/media/v4l2-core/v4l2-common.c
>> +++ b/drivers/media/v4l2-core/v4l2-common.c
>> @@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
>> tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
>> }
>> EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
>> +
>> +char *v4l2_fourcc2s(u32 fourcc, char *buf)
>> +{
>> + buf[0] = fourcc & 0x7f;
>> + buf[1] = (fourcc >> 8) & 0x7f;
>> + buf[2] = (fourcc >> 16) & 0x7f;
>> + buf[3] = (fourcc >> 24) & 0x7f;
>> + if (fourcc & (1 << 31)) {
>> + buf[4] = '-';
>> + buf[5] = 'B';
>> + buf[6] = 'E';
>> + buf[7] = '\0';
>> + } else {
>> + buf[4] = '\0';
>> + }
>> + return buf;
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
>> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
>> index aac8b7b..5b0fff9 100644
>> --- a/include/media/v4l2-common.h
>> +++ b/include/media/v4l2-common.h
>> @@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
>>
>> void v4l2_get_timestamp(struct timeval *tv);
>>
>> +#define V4L2_FOURCC_MAX_SIZE 8
>> +char *v4l2_fourcc2s(u32 fourcc, char *buf);
>> +
>> #endif /* V4L2_COMMON_H_ */
>
> I like the idea but the use of a character pointer and assuming a length
> looks a bit scary.
>
> As this seems to be used uniquely for printing stuff, a couple of macros
> could be used instead. Something like
>
> #define V4L2_FOURCC_CONV "%c%c%c%c%s"
> #define V4L2_FOURCC_TO_CONV(fourcc) \
> fourcc & 0x7f, (fourcc >> 8) & 0x7f, (fourcc >> 16) & 0x7f, \
> (fourcc >> 24) & 0x7f, fourcc & BIT(31) ? "-BE" : ""
>
> You could use it with printk-style functions, e.g.
>
> printk("blah fourcc " V4L2_FOURCC_CONV " is a nice format",
> V4L2_FOURCC_TO_CONV(fourcc));
>
> I guess it'd be better to add more parentheses around "fourcc" but it'd be
> less clear here that way.
I was following Hans' suggestion made in
https://www.spinics.net/lists/linux-media/msg117046.html
Hans: Do you agree with Sakari here to make it to a macro?
Dave
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
2017-10-17 9:17 ` Dave Stevenson
@ 2017-10-17 9:28 ` Hans Verkuil
0 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2017-10-17 9:28 UTC (permalink / raw)
To: Dave Stevenson, Sakari Ailus, Hans Verkuil
Cc: Mauro Carvalho Chehab, Rob Herring, Eric Anholt, Stefan Wahren,
linux-media-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
On 10/17/17 11:17, Dave Stevenson wrote:
> Hi Sakari.
>
> On 13 October 2017 at 22:09, Sakari Ailus <sakari.ailus-X3B1VOXEql0@public.gmane.org> wrote:
>> Hi Dave,
>>
>> On Wed, Sep 20, 2017 at 05:07:54PM +0100, Dave Stevenson wrote:
>>> New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
>>> that converts a fourcc into a nice printable version.
>>>
>>> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
>>> ---
>>>
>>> No changes from v2 to v3
>>>
>>> drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
>>> include/media/v4l2-common.h | 3 +++
>>> 2 files changed, 21 insertions(+)
>>>
>>> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
>>> index a5ea1f5..0219895 100644
>>> --- a/drivers/media/v4l2-core/v4l2-common.c
>>> +++ b/drivers/media/v4l2-core/v4l2-common.c
>>> @@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
>>> tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
>>> }
>>> EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
>>> +
>>> +char *v4l2_fourcc2s(u32 fourcc, char *buf)
>>> +{
>>> + buf[0] = fourcc & 0x7f;
>>> + buf[1] = (fourcc >> 8) & 0x7f;
>>> + buf[2] = (fourcc >> 16) & 0x7f;
>>> + buf[3] = (fourcc >> 24) & 0x7f;
>>> + if (fourcc & (1 << 31)) {
>>> + buf[4] = '-';
>>> + buf[5] = 'B';
>>> + buf[6] = 'E';
>>> + buf[7] = '\0';
>>> + } else {
>>> + buf[4] = '\0';
>>> + }
>>> + return buf;
>>> +}
>>> +EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
>>> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
>>> index aac8b7b..5b0fff9 100644
>>> --- a/include/media/v4l2-common.h
>>> +++ b/include/media/v4l2-common.h
>>> @@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
>>>
>>> void v4l2_get_timestamp(struct timeval *tv);
>>>
>>> +#define V4L2_FOURCC_MAX_SIZE 8
>>> +char *v4l2_fourcc2s(u32 fourcc, char *buf);
>>> +
>>> #endif /* V4L2_COMMON_H_ */
>>
>> I like the idea but the use of a character pointer and assuming a length
>> looks a bit scary.
>>
>> As this seems to be used uniquely for printing stuff, a couple of macros
>> could be used instead. Something like
>>
>> #define V4L2_FOURCC_CONV "%c%c%c%c%s"
>> #define V4L2_FOURCC_TO_CONV(fourcc) \
>> fourcc & 0x7f, (fourcc >> 8) & 0x7f, (fourcc >> 16) & 0x7f, \
>> (fourcc >> 24) & 0x7f, fourcc & BIT(31) ? "-BE" : ""
'fourcc' should be in parenthesis '(fourcc)'.
>>
>> You could use it with printk-style functions, e.g.
>>
>> printk("blah fourcc " V4L2_FOURCC_CONV " is a nice format",
>> V4L2_FOURCC_TO_CONV(fourcc));
>>
>> I guess it'd be better to add more parentheses around "fourcc" but it'd be
>> less clear here that way.
>
> I was following Hans' suggestion made in
> https://www.spinics.net/lists/linux-media/msg117046.html
>
> Hans: Do you agree with Sakari here to make it to a macro?
Not a bad idea. But I think we should add it to the public videodev2.h
header in that case, allowing it to be used by applications as well.
How about calling the defines V4L2_FOURCC_FMT and V4L2_FOURCC_FMT_ARGS?
Regards,
Hans
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
@ 2017-10-17 9:28 ` Hans Verkuil
0 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2017-10-17 9:28 UTC (permalink / raw)
To: Dave Stevenson, Sakari Ailus, Hans Verkuil
Cc: Mauro Carvalho Chehab, Rob Herring, Eric Anholt, Stefan Wahren,
linux-media, linux-rpi-kernel, devicetree
On 10/17/17 11:17, Dave Stevenson wrote:
> Hi Sakari.
>
> On 13 October 2017 at 22:09, Sakari Ailus <sakari.ailus@iki.fi> wrote:
>> Hi Dave,
>>
>> On Wed, Sep 20, 2017 at 05:07:54PM +0100, Dave Stevenson wrote:
>>> New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
>>> that converts a fourcc into a nice printable version.
>>>
>>> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
>>> ---
>>>
>>> No changes from v2 to v3
>>>
>>> drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
>>> include/media/v4l2-common.h | 3 +++
>>> 2 files changed, 21 insertions(+)
>>>
>>> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
>>> index a5ea1f5..0219895 100644
>>> --- a/drivers/media/v4l2-core/v4l2-common.c
>>> +++ b/drivers/media/v4l2-core/v4l2-common.c
>>> @@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
>>> tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
>>> }
>>> EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
>>> +
>>> +char *v4l2_fourcc2s(u32 fourcc, char *buf)
>>> +{
>>> + buf[0] = fourcc & 0x7f;
>>> + buf[1] = (fourcc >> 8) & 0x7f;
>>> + buf[2] = (fourcc >> 16) & 0x7f;
>>> + buf[3] = (fourcc >> 24) & 0x7f;
>>> + if (fourcc & (1 << 31)) {
>>> + buf[4] = '-';
>>> + buf[5] = 'B';
>>> + buf[6] = 'E';
>>> + buf[7] = '\0';
>>> + } else {
>>> + buf[4] = '\0';
>>> + }
>>> + return buf;
>>> +}
>>> +EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
>>> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
>>> index aac8b7b..5b0fff9 100644
>>> --- a/include/media/v4l2-common.h
>>> +++ b/include/media/v4l2-common.h
>>> @@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
>>>
>>> void v4l2_get_timestamp(struct timeval *tv);
>>>
>>> +#define V4L2_FOURCC_MAX_SIZE 8
>>> +char *v4l2_fourcc2s(u32 fourcc, char *buf);
>>> +
>>> #endif /* V4L2_COMMON_H_ */
>>
>> I like the idea but the use of a character pointer and assuming a length
>> looks a bit scary.
>>
>> As this seems to be used uniquely for printing stuff, a couple of macros
>> could be used instead. Something like
>>
>> #define V4L2_FOURCC_CONV "%c%c%c%c%s"
>> #define V4L2_FOURCC_TO_CONV(fourcc) \
>> fourcc & 0x7f, (fourcc >> 8) & 0x7f, (fourcc >> 16) & 0x7f, \
>> (fourcc >> 24) & 0x7f, fourcc & BIT(31) ? "-BE" : ""
'fourcc' should be in parenthesis '(fourcc)'.
>>
>> You could use it with printk-style functions, e.g.
>>
>> printk("blah fourcc " V4L2_FOURCC_CONV " is a nice format",
>> V4L2_FOURCC_TO_CONV(fourcc));
>>
>> I guess it'd be better to add more parentheses around "fourcc" but it'd be
>> less clear here that way.
>
> I was following Hans' suggestion made in
> https://www.spinics.net/lists/linux-media/msg117046.html
>
> Hans: Do you agree with Sakari here to make it to a macro?
Not a bad idea. But I think we should add it to the public videodev2.h
header in that case, allowing it to be used by applications as well.
How about calling the defines V4L2_FOURCC_FMT and V4L2_FOURCC_FMT_ARGS?
Regards,
Hans
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
2017-10-17 9:28 ` Hans Verkuil
@ 2017-10-17 10:08 ` Sakari Ailus
-1 siblings, 0 replies; 30+ messages in thread
From: Sakari Ailus @ 2017-10-17 10:08 UTC (permalink / raw)
To: Hans Verkuil
Cc: Dave Stevenson, Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
Eric Anholt, Stefan Wahren, linux-media-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
Hi Hans,
On Tue, Oct 17, 2017 at 11:28:46AM +0200, Hans Verkuil wrote:
> On 10/17/17 11:17, Dave Stevenson wrote:
> > Hi Sakari.
> >
> > On 13 October 2017 at 22:09, Sakari Ailus <sakari.ailus-X3B1VOXEql0@public.gmane.org> wrote:
> >> Hi Dave,
> >>
> >> On Wed, Sep 20, 2017 at 05:07:54PM +0100, Dave Stevenson wrote:
> >>> New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
> >>> that converts a fourcc into a nice printable version.
> >>>
> >>> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
> >>> ---
> >>>
> >>> No changes from v2 to v3
> >>>
> >>> drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
> >>> include/media/v4l2-common.h | 3 +++
> >>> 2 files changed, 21 insertions(+)
> >>>
> >>> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> >>> index a5ea1f5..0219895 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-common.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-common.c
> >>> @@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
> >>> tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
> >>> }
> >>> EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
> >>> +
> >>> +char *v4l2_fourcc2s(u32 fourcc, char *buf)
> >>> +{
> >>> + buf[0] = fourcc & 0x7f;
> >>> + buf[1] = (fourcc >> 8) & 0x7f;
> >>> + buf[2] = (fourcc >> 16) & 0x7f;
> >>> + buf[3] = (fourcc >> 24) & 0x7f;
> >>> + if (fourcc & (1 << 31)) {
> >>> + buf[4] = '-';
> >>> + buf[5] = 'B';
> >>> + buf[6] = 'E';
> >>> + buf[7] = '\0';
> >>> + } else {
> >>> + buf[4] = '\0';
> >>> + }
> >>> + return buf;
> >>> +}
> >>> +EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
> >>> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
> >>> index aac8b7b..5b0fff9 100644
> >>> --- a/include/media/v4l2-common.h
> >>> +++ b/include/media/v4l2-common.h
> >>> @@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
> >>>
> >>> void v4l2_get_timestamp(struct timeval *tv);
> >>>
> >>> +#define V4L2_FOURCC_MAX_SIZE 8
> >>> +char *v4l2_fourcc2s(u32 fourcc, char *buf);
> >>> +
> >>> #endif /* V4L2_COMMON_H_ */
> >>
> >> I like the idea but the use of a character pointer and assuming a length
> >> looks a bit scary.
> >>
> >> As this seems to be used uniquely for printing stuff, a couple of macros
> >> could be used instead. Something like
> >>
> >> #define V4L2_FOURCC_CONV "%c%c%c%c%s"
> >> #define V4L2_FOURCC_TO_CONV(fourcc) \
> >> fourcc & 0x7f, (fourcc >> 8) & 0x7f, (fourcc >> 16) & 0x7f, \
> >> (fourcc >> 24) & 0x7f, fourcc & BIT(31) ? "-BE" : ""
>
> 'fourcc' should be in parenthesis '(fourcc)'.
Yes, I omitted those for readability here.
>
> >>
> >> You could use it with printk-style functions, e.g.
> >>
> >> printk("blah fourcc " V4L2_FOURCC_CONV " is a nice format",
> >> V4L2_FOURCC_TO_CONV(fourcc));
> >>
> >> I guess it'd be better to add more parentheses around "fourcc" but it'd be
> >> less clear here that way.
> >
> > I was following Hans' suggestion made in
> > https://www.spinics.net/lists/linux-media/msg117046.html
> >
> > Hans: Do you agree with Sakari here to make it to a macro?
>
> Not a bad idea. But I think we should add it to the public videodev2.h
> header in that case, allowing it to be used by applications as well.
Sounds good.
>
> How about calling the defines V4L2_FOURCC_FMT and V4L2_FOURCC_FMT_ARGS?
printf(3) describes conversion specifiers (%...) and I thought of using
"CONV" there for that purpose to make it explicit. Fourcc, implicitly,
already is about formats. What would you think of V4L2_FOURCC_CONV and
V4L2_FOURCC_CONV_ARGS (or just V4L2_FOURCC_ARGS)?
--
Kind regards,
Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string
@ 2017-10-17 10:08 ` Sakari Ailus
0 siblings, 0 replies; 30+ messages in thread
From: Sakari Ailus @ 2017-10-17 10:08 UTC (permalink / raw)
To: Hans Verkuil
Cc: Dave Stevenson, Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
Eric Anholt, Stefan Wahren, linux-media, linux-rpi-kernel,
devicetree
Hi Hans,
On Tue, Oct 17, 2017 at 11:28:46AM +0200, Hans Verkuil wrote:
> On 10/17/17 11:17, Dave Stevenson wrote:
> > Hi Sakari.
> >
> > On 13 October 2017 at 22:09, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> >> Hi Dave,
> >>
> >> On Wed, Sep 20, 2017 at 05:07:54PM +0100, Dave Stevenson wrote:
> >>> New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
> >>> that converts a fourcc into a nice printable version.
> >>>
> >>> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
> >>> ---
> >>>
> >>> No changes from v2 to v3
> >>>
> >>> drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
> >>> include/media/v4l2-common.h | 3 +++
> >>> 2 files changed, 21 insertions(+)
> >>>
> >>> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> >>> index a5ea1f5..0219895 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-common.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-common.c
> >>> @@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
> >>> tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
> >>> }
> >>> EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
> >>> +
> >>> +char *v4l2_fourcc2s(u32 fourcc, char *buf)
> >>> +{
> >>> + buf[0] = fourcc & 0x7f;
> >>> + buf[1] = (fourcc >> 8) & 0x7f;
> >>> + buf[2] = (fourcc >> 16) & 0x7f;
> >>> + buf[3] = (fourcc >> 24) & 0x7f;
> >>> + if (fourcc & (1 << 31)) {
> >>> + buf[4] = '-';
> >>> + buf[5] = 'B';
> >>> + buf[6] = 'E';
> >>> + buf[7] = '\0';
> >>> + } else {
> >>> + buf[4] = '\0';
> >>> + }
> >>> + return buf;
> >>> +}
> >>> +EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
> >>> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
> >>> index aac8b7b..5b0fff9 100644
> >>> --- a/include/media/v4l2-common.h
> >>> +++ b/include/media/v4l2-common.h
> >>> @@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
> >>>
> >>> void v4l2_get_timestamp(struct timeval *tv);
> >>>
> >>> +#define V4L2_FOURCC_MAX_SIZE 8
> >>> +char *v4l2_fourcc2s(u32 fourcc, char *buf);
> >>> +
> >>> #endif /* V4L2_COMMON_H_ */
> >>
> >> I like the idea but the use of a character pointer and assuming a length
> >> looks a bit scary.
> >>
> >> As this seems to be used uniquely for printing stuff, a couple of macros
> >> could be used instead. Something like
> >>
> >> #define V4L2_FOURCC_CONV "%c%c%c%c%s"
> >> #define V4L2_FOURCC_TO_CONV(fourcc) \
> >> fourcc & 0x7f, (fourcc >> 8) & 0x7f, (fourcc >> 16) & 0x7f, \
> >> (fourcc >> 24) & 0x7f, fourcc & BIT(31) ? "-BE" : ""
>
> 'fourcc' should be in parenthesis '(fourcc)'.
Yes, I omitted those for readability here.
>
> >>
> >> You could use it with printk-style functions, e.g.
> >>
> >> printk("blah fourcc " V4L2_FOURCC_CONV " is a nice format",
> >> V4L2_FOURCC_TO_CONV(fourcc));
> >>
> >> I guess it'd be better to add more parentheses around "fourcc" but it'd be
> >> less clear here that way.
> >
> > I was following Hans' suggestion made in
> > https://www.spinics.net/lists/linux-media/msg117046.html
> >
> > Hans: Do you agree with Sakari here to make it to a macro?
>
> Not a bad idea. But I think we should add it to the public videodev2.h
> header in that case, allowing it to be used by applications as well.
Sounds good.
>
> How about calling the defines V4L2_FOURCC_FMT and V4L2_FOURCC_FMT_ARGS?
printf(3) describes conversion specifiers (%...) and I thought of using
"CONV" there for that purpose to make it explicit. Fourcc, implicitly,
already is about formats. What would you think of V4L2_FOURCC_CONV and
V4L2_FOURCC_CONV_ARGS (or just V4L2_FOURCC_ARGS)?
--
Kind regards,
Sakari Ailus
e-mail: sakari.ailus@iki.fi
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-10-02 10:36 ` Dave Stevenson
@ 2017-11-21 19:26 ` Eric Anholt
0 siblings, 0 replies; 30+ messages in thread
From: Eric Anholt @ 2017-11-21 19:26 UTC (permalink / raw)
To: Dave Stevenson, Rob Herring
Cc: Stefan Wahren, Mauro Carvalho Chehab,
linux-media-u79uwXL29TY76Z2rM5mHXA, Sakari Ailus, Hans Verkuil,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA
[-- Attachment #1: Type: text/plain, Size: 6120 bytes --]
Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> writes:
> Hi Rob
>
> On 27 September 2017 at 22:51, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>> On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
>>> Hi Stefan
>>>
>>> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org> wrote:
>>> > Hi Dave,
>>> >
>>> >> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> hat am 20. September 2017 um 18:07 geschrieben:
>>> >>
>>> >>
>>> >> Document the DT bindings for the CSI2/CCP2 receiver peripheral
>>> >> (known as Unicam) on BCM283x SoCs.
>>> >>
>>> >> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
>>> >> ---
>>> >>
>>> >> Changes since v2
>>> >> - Removed all references to Linux drivers.
>>> >> - Reworded section about disabling the firmware driver.
>>> >> - Renamed clock from "lp_clock" to "lp" in description and example.
>>> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>> >> and data-lanes.
>>> >> - Corrected typo in example from csi to csi1.
>>> >> - Removed unnecessary #address-cells and #size-cells in example.
>>> >> - Removed setting of status from the example.
>>> >>
>>> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>> >> 1 file changed, 85 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..7714fb3
>>> >> --- /dev/null
>>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>> >> @@ -0,0 +1,85 @@
>>> >> +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.
>>> >> +
>>> >> +The main platform using this SoC is the Raspberry Pi family of boards.
>>> >> +On the Pi the VideoCore firmware can also control this hardware block,
>>> >> +and driving it from two different processors will cause issues.
>>> >> +To avoid this, the firmware checks the device tree configuration
>>> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>> >> +it will stop the firmware accessing the block, and it can then
>>> >> +safely be used via the device tree binding.
>>> >> +
>>> >> +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" entry, matching entries in the
>>> >> + clocks property.
>>> >> +
>>> >> +Unicam supports a single port node. It should contain one 'port' child node
>>> >> +with child 'endpoint' node. Please refer to the bindings defined in
>>> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>> >> +
>>> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>> >> +are mandatory.
>>> >> +Data lane reordering is not supported so the data lanes must be in order,
>>> >> +starting at 1. The number of data lanes should represent the number of
>>> >> +usable lanes for the hardware block. That may be limited by either the SoC or
>>> >> +how the platform presents the interface, and the lower value must be used.
>>> >> +
>>> >> +Lane reordering is not supported on the clock lane either, so the optional
>>> >> +property "clock-lane" will implicitly be <0>.
>>> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>> >> +implicitly be <0 0 0 0 0>.
>>> >> +Neither of these values will be checked.
>>> >> +
>>> >> +Example:
>>> >> + csi1: csi1@7e801000 {
>>> >> + compatible = "brcm,bcm2835-unicam";
>>> >> + reg = <0x7e801000 0x800>,
>>> >> + <0x7e802004 0x4>;
>>> >
>>> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>>>
>>> CMI (Clock Manager Image) consists of a total of 4 registers.
>>> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
>>> inversion of the clock and data lanes (2 data lanes available on
>>> CAM0).
>>> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
>>> inversion of the clock and data lanes (4 data lanes available on
>>> CAM1).
>>> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
>>> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
>>> default value is the required value. Nothing touches it that I can
>>> find.
>>>
>>> The range listed only covers the one register associated with that
>>> Unicam instance, so no other users. The other two aren't touched.
>>> Do 16 active register bits solely for camera clock gating really
>>> warrant a full clock driver?
>>
>> You should describe all the registers in DT, not just what the driver
>> (currently) uses.
>
> I'm not clear what you're asking for here.
>
> This binding is for the Unicam block, not for CMI (Clock Manager
> Imaging). In order for a Unicam instance to work, it needs to enable
> the relevant clock gating via 1 CMI register, and it will only ever be
> one register.
Rob, the CMI just a small bit of glue required by the crossing of a
power domain in a unicam instance, and the two unicam instances in this
HW have their CMI regs next to each other. It's not really a separate
block, and I think describing the unicam's CMI reg in the unicam binding
is appropriate.
What would you need from Dave to ack this binding?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-11-21 19:26 ` Eric Anholt
0 siblings, 0 replies; 30+ messages in thread
From: Eric Anholt @ 2017-11-21 19:26 UTC (permalink / raw)
To: Dave Stevenson, Rob Herring
Cc: Stefan Wahren, Mauro Carvalho Chehab, linux-media, Sakari Ailus,
Hans Verkuil, linux-rpi-kernel, devicetree
[-- Attachment #1: Type: text/plain, Size: 5999 bytes --]
Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
> Hi Rob
>
> On 27 September 2017 at 22:51, Rob Herring <robh@kernel.org> wrote:
>> On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
>>> Hi Stefan
>>>
>>> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>>> > Hi Dave,
>>> >
>>> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>>> >>
>>> >>
>>> >> 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>
>>> >> ---
>>> >>
>>> >> Changes since v2
>>> >> - Removed all references to Linux drivers.
>>> >> - Reworded section about disabling the firmware driver.
>>> >> - Renamed clock from "lp_clock" to "lp" in description and example.
>>> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>> >> and data-lanes.
>>> >> - Corrected typo in example from csi to csi1.
>>> >> - Removed unnecessary #address-cells and #size-cells in example.
>>> >> - Removed setting of status from the example.
>>> >>
>>> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>> >> 1 file changed, 85 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..7714fb3
>>> >> --- /dev/null
>>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>> >> @@ -0,0 +1,85 @@
>>> >> +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.
>>> >> +
>>> >> +The main platform using this SoC is the Raspberry Pi family of boards.
>>> >> +On the Pi the VideoCore firmware can also control this hardware block,
>>> >> +and driving it from two different processors will cause issues.
>>> >> +To avoid this, the firmware checks the device tree configuration
>>> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>> >> +it will stop the firmware accessing the block, and it can then
>>> >> +safely be used via the device tree binding.
>>> >> +
>>> >> +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" entry, matching entries in the
>>> >> + clocks property.
>>> >> +
>>> >> +Unicam supports a single port node. It should contain one 'port' child node
>>> >> +with child 'endpoint' node. Please refer to the bindings defined in
>>> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>> >> +
>>> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>> >> +are mandatory.
>>> >> +Data lane reordering is not supported so the data lanes must be in order,
>>> >> +starting at 1. The number of data lanes should represent the number of
>>> >> +usable lanes for the hardware block. That may be limited by either the SoC or
>>> >> +how the platform presents the interface, and the lower value must be used.
>>> >> +
>>> >> +Lane reordering is not supported on the clock lane either, so the optional
>>> >> +property "clock-lane" will implicitly be <0>.
>>> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>> >> +implicitly be <0 0 0 0 0>.
>>> >> +Neither of these values will be checked.
>>> >> +
>>> >> +Example:
>>> >> + csi1: csi1@7e801000 {
>>> >> + compatible = "brcm,bcm2835-unicam";
>>> >> + reg = <0x7e801000 0x800>,
>>> >> + <0x7e802004 0x4>;
>>> >
>>> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>>>
>>> CMI (Clock Manager Image) consists of a total of 4 registers.
>>> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
>>> inversion of the clock and data lanes (2 data lanes available on
>>> CAM0).
>>> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
>>> inversion of the clock and data lanes (4 data lanes available on
>>> CAM1).
>>> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
>>> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
>>> default value is the required value. Nothing touches it that I can
>>> find.
>>>
>>> The range listed only covers the one register associated with that
>>> Unicam instance, so no other users. The other two aren't touched.
>>> Do 16 active register bits solely for camera clock gating really
>>> warrant a full clock driver?
>>
>> You should describe all the registers in DT, not just what the driver
>> (currently) uses.
>
> I'm not clear what you're asking for here.
>
> This binding is for the Unicam block, not for CMI (Clock Manager
> Imaging). In order for a Unicam instance to work, it needs to enable
> the relevant clock gating via 1 CMI register, and it will only ever be
> one register.
Rob, the CMI just a small bit of glue required by the crossing of a
power domain in a unicam instance, and the two unicam instances in this
HW have their CMI regs next to each other. It's not really a separate
block, and I think describing the unicam's CMI reg in the unicam binding
is appropriate.
What would you need from Dave to ack this binding?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-11-21 19:26 ` Eric Anholt
(?)
@ 2017-11-21 19:58 ` Rob Herring
2017-11-21 20:54 ` Eric Anholt
-1 siblings, 1 reply; 30+ messages in thread
From: Rob Herring @ 2017-11-21 19:58 UTC (permalink / raw)
To: Eric Anholt
Cc: Dave Stevenson, Stefan Wahren, Mauro Carvalho Chehab,
linux-media, Sakari Ailus, Hans Verkuil,
moderated list:BROADCOM BCM2835 ARM ARCHITECTURE, devicetree
On Tue, Nov 21, 2017 at 1:26 PM, Eric Anholt <eric@anholt.net> wrote:
> Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
>
>> Hi Rob
>>
>> On 27 September 2017 at 22:51, Rob Herring <robh@kernel.org> wrote:
>>> On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
>>>> Hi Stefan
>>>>
>>>> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>>>> > Hi Dave,
>>>> >
>>>> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>>>> >>
>>>> >>
>>>> >> 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>
>>>> >> ---
>>>> >>
>>>> >> Changes since v2
>>>> >> - Removed all references to Linux drivers.
>>>> >> - Reworded section about disabling the firmware driver.
>>>> >> - Renamed clock from "lp_clock" to "lp" in description and example.
>>>> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>>> >> and data-lanes.
>>>> >> - Corrected typo in example from csi to csi1.
>>>> >> - Removed unnecessary #address-cells and #size-cells in example.
>>>> >> - Removed setting of status from the example.
>>>> >>
>>>> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>>> >> 1 file changed, 85 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..7714fb3
>>>> >> --- /dev/null
>>>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>>> >> @@ -0,0 +1,85 @@
>>>> >> +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.
>>>> >> +
>>>> >> +The main platform using this SoC is the Raspberry Pi family of boards.
>>>> >> +On the Pi the VideoCore firmware can also control this hardware block,
>>>> >> +and driving it from two different processors will cause issues.
>>>> >> +To avoid this, the firmware checks the device tree configuration
>>>> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>>> >> +it will stop the firmware accessing the block, and it can then
>>>> >> +safely be used via the device tree binding.
>>>> >> +
>>>> >> +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" entry, matching entries in the
>>>> >> + clocks property.
>>>> >> +
>>>> >> +Unicam supports a single port node. It should contain one 'port' child node
>>>> >> +with child 'endpoint' node. Please refer to the bindings defined in
>>>> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>>> >> +
>>>> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>>> >> +are mandatory.
>>>> >> +Data lane reordering is not supported so the data lanes must be in order,
>>>> >> +starting at 1. The number of data lanes should represent the number of
>>>> >> +usable lanes for the hardware block. That may be limited by either the SoC or
>>>> >> +how the platform presents the interface, and the lower value must be used.
>>>> >> +
>>>> >> +Lane reordering is not supported on the clock lane either, so the optional
>>>> >> +property "clock-lane" will implicitly be <0>.
>>>> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>>> >> +implicitly be <0 0 0 0 0>.
>>>> >> +Neither of these values will be checked.
>>>> >> +
>>>> >> +Example:
>>>> >> + csi1: csi1@7e801000 {
>>>> >> + compatible = "brcm,bcm2835-unicam";
>>>> >> + reg = <0x7e801000 0x800>,
>>>> >> + <0x7e802004 0x4>;
>>>> >
>>>> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>>>>
>>>> CMI (Clock Manager Image) consists of a total of 4 registers.
>>>> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
>>>> inversion of the clock and data lanes (2 data lanes available on
>>>> CAM0).
>>>> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
>>>> inversion of the clock and data lanes (4 data lanes available on
>>>> CAM1).
>>>> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
>>>> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
>>>> default value is the required value. Nothing touches it that I can
>>>> find.
>>>>
>>>> The range listed only covers the one register associated with that
>>>> Unicam instance, so no other users. The other two aren't touched.
>>>> Do 16 active register bits solely for camera clock gating really
>>>> warrant a full clock driver?
>>>
>>> You should describe all the registers in DT, not just what the driver
>>> (currently) uses.
>>
>> I'm not clear what you're asking for here.
>>
>> This binding is for the Unicam block, not for CMI (Clock Manager
>> Imaging). In order for a Unicam instance to work, it needs to enable
>> the relevant clock gating via 1 CMI register, and it will only ever be
>> one register.
>
> Rob, the CMI just a small bit of glue required by the crossing of a
> power domain in a unicam instance, and the two unicam instances in this
> HW have their CMI regs next to each other. It's not really a separate
> block, and I think describing the unicam's CMI reg in the unicam binding
> is appropriate.
>
> What would you need from Dave to ack this binding?
Sorry, had started to reply on this and got distracted.
I guess since there seems to be only 1 other bit that could possibly
be used (CMI_USBCTL) it is fine like this. However, my concern would
be if the CMI registers are integrated in a different way in some
future chip that has the same unicam instances. Or just if the
register bits are rearranged. Those are not an uncommon occurrence.
You would have to provide access to those registers in some other way.
It can be dealt with, but then you have to support both cases in the
driver. If you all are fine with that, then:
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-11-21 19:58 ` Rob Herring
@ 2017-11-21 20:54 ` Eric Anholt
0 siblings, 0 replies; 30+ messages in thread
From: Eric Anholt @ 2017-11-21 20:54 UTC (permalink / raw)
To: Rob Herring
Cc: Dave Stevenson, Stefan Wahren, Mauro Carvalho Chehab,
linux-media, Sakari Ailus, Hans Verkuil,
moderated list:BROADCOM BCM2835 ARM ARCHITECTURE, devicetree
[-- Attachment #1: Type: text/plain, Size: 7212 bytes --]
Rob Herring <robh@kernel.org> writes:
> On Tue, Nov 21, 2017 at 1:26 PM, Eric Anholt <eric@anholt.net> wrote:
>> Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
>>
>>> Hi Rob
>>>
>>> On 27 September 2017 at 22:51, Rob Herring <robh@kernel.org> wrote:
>>>> On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
>>>>> Hi Stefan
>>>>>
>>>>> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>>>>> > Hi Dave,
>>>>> >
>>>>> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>>>>> >>
>>>>> >>
>>>>> >> 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>
>>>>> >> ---
>>>>> >>
>>>>> >> Changes since v2
>>>>> >> - Removed all references to Linux drivers.
>>>>> >> - Reworded section about disabling the firmware driver.
>>>>> >> - Renamed clock from "lp_clock" to "lp" in description and example.
>>>>> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>>>> >> and data-lanes.
>>>>> >> - Corrected typo in example from csi to csi1.
>>>>> >> - Removed unnecessary #address-cells and #size-cells in example.
>>>>> >> - Removed setting of status from the example.
>>>>> >>
>>>>> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>>>> >> 1 file changed, 85 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..7714fb3
>>>>> >> --- /dev/null
>>>>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>>>> >> @@ -0,0 +1,85 @@
>>>>> >> +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.
>>>>> >> +
>>>>> >> +The main platform using this SoC is the Raspberry Pi family of boards.
>>>>> >> +On the Pi the VideoCore firmware can also control this hardware block,
>>>>> >> +and driving it from two different processors will cause issues.
>>>>> >> +To avoid this, the firmware checks the device tree configuration
>>>>> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>>>> >> +it will stop the firmware accessing the block, and it can then
>>>>> >> +safely be used via the device tree binding.
>>>>> >> +
>>>>> >> +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" entry, matching entries in the
>>>>> >> + clocks property.
>>>>> >> +
>>>>> >> +Unicam supports a single port node. It should contain one 'port' child node
>>>>> >> +with child 'endpoint' node. Please refer to the bindings defined in
>>>>> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>>>> >> +
>>>>> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>>>> >> +are mandatory.
>>>>> >> +Data lane reordering is not supported so the data lanes must be in order,
>>>>> >> +starting at 1. The number of data lanes should represent the number of
>>>>> >> +usable lanes for the hardware block. That may be limited by either the SoC or
>>>>> >> +how the platform presents the interface, and the lower value must be used.
>>>>> >> +
>>>>> >> +Lane reordering is not supported on the clock lane either, so the optional
>>>>> >> +property "clock-lane" will implicitly be <0>.
>>>>> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>>>> >> +implicitly be <0 0 0 0 0>.
>>>>> >> +Neither of these values will be checked.
>>>>> >> +
>>>>> >> +Example:
>>>>> >> + csi1: csi1@7e801000 {
>>>>> >> + compatible = "brcm,bcm2835-unicam";
>>>>> >> + reg = <0x7e801000 0x800>,
>>>>> >> + <0x7e802004 0x4>;
>>>>> >
>>>>> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>>>>>
>>>>> CMI (Clock Manager Image) consists of a total of 4 registers.
>>>>> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
>>>>> inversion of the clock and data lanes (2 data lanes available on
>>>>> CAM0).
>>>>> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
>>>>> inversion of the clock and data lanes (4 data lanes available on
>>>>> CAM1).
>>>>> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
>>>>> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
>>>>> default value is the required value. Nothing touches it that I can
>>>>> find.
>>>>>
>>>>> The range listed only covers the one register associated with that
>>>>> Unicam instance, so no other users. The other two aren't touched.
>>>>> Do 16 active register bits solely for camera clock gating really
>>>>> warrant a full clock driver?
>>>>
>>>> You should describe all the registers in DT, not just what the driver
>>>> (currently) uses.
>>>
>>> I'm not clear what you're asking for here.
>>>
>>> This binding is for the Unicam block, not for CMI (Clock Manager
>>> Imaging). In order for a Unicam instance to work, it needs to enable
>>> the relevant clock gating via 1 CMI register, and it will only ever be
>>> one register.
>>
>> Rob, the CMI just a small bit of glue required by the crossing of a
>> power domain in a unicam instance, and the two unicam instances in this
>> HW have their CMI regs next to each other. It's not really a separate
>> block, and I think describing the unicam's CMI reg in the unicam binding
>> is appropriate.
>>
>> What would you need from Dave to ack this binding?
>
> Sorry, had started to reply on this and got distracted.
>
> I guess since there seems to be only 1 other bit that could possibly
> be used (CMI_USBCTL) it is fine like this. However, my concern would
> be if the CMI registers are integrated in a different way in some
> future chip that has the same unicam instances. Or just if the
> register bits are rearranged. Those are not an uncommon occurrence.
> You would have to provide access to those registers in some other way.
> It can be dealt with, but then you have to support both cases in the
> driver. If you all are fine with that, then:
The bigisland chips match bcm2835. For capri the lane enables are
shifted down by two and the clock is up at bit 20. That would be
trivial to handle based on the compatible string, except that we can't
talk to capri's hardware from ARM anyway :(
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-11-21 20:54 ` Eric Anholt
0 siblings, 0 replies; 30+ messages in thread
From: Eric Anholt @ 2017-11-21 20:54 UTC (permalink / raw)
To: Rob Herring
Cc: Dave Stevenson, Stefan Wahren, Mauro Carvalho Chehab,
linux-media, Sakari Ailus, Hans Verkuil,
moderated list:BROADCOM BCM2835 ARM ARCHITECTURE, devicetree
[-- Attachment #1: Type: text/plain, Size: 7212 bytes --]
Rob Herring <robh@kernel.org> writes:
> On Tue, Nov 21, 2017 at 1:26 PM, Eric Anholt <eric@anholt.net> wrote:
>> Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
>>
>>> Hi Rob
>>>
>>> On 27 September 2017 at 22:51, Rob Herring <robh@kernel.org> wrote:
>>>> On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
>>>>> Hi Stefan
>>>>>
>>>>> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>>>>> > Hi Dave,
>>>>> >
>>>>> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>>>>> >>
>>>>> >>
>>>>> >> 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>
>>>>> >> ---
>>>>> >>
>>>>> >> Changes since v2
>>>>> >> - Removed all references to Linux drivers.
>>>>> >> - Reworded section about disabling the firmware driver.
>>>>> >> - Renamed clock from "lp_clock" to "lp" in description and example.
>>>>> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>>>> >> and data-lanes.
>>>>> >> - Corrected typo in example from csi to csi1.
>>>>> >> - Removed unnecessary #address-cells and #size-cells in example.
>>>>> >> - Removed setting of status from the example.
>>>>> >>
>>>>> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>>>> >> 1 file changed, 85 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..7714fb3
>>>>> >> --- /dev/null
>>>>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>>>> >> @@ -0,0 +1,85 @@
>>>>> >> +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.
>>>>> >> +
>>>>> >> +The main platform using this SoC is the Raspberry Pi family of boards.
>>>>> >> +On the Pi the VideoCore firmware can also control this hardware block,
>>>>> >> +and driving it from two different processors will cause issues.
>>>>> >> +To avoid this, the firmware checks the device tree configuration
>>>>> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>>>> >> +it will stop the firmware accessing the block, and it can then
>>>>> >> +safely be used via the device tree binding.
>>>>> >> +
>>>>> >> +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" entry, matching entries in the
>>>>> >> + clocks property.
>>>>> >> +
>>>>> >> +Unicam supports a single port node. It should contain one 'port' child node
>>>>> >> +with child 'endpoint' node. Please refer to the bindings defined in
>>>>> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>>>> >> +
>>>>> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>>>> >> +are mandatory.
>>>>> >> +Data lane reordering is not supported so the data lanes must be in order,
>>>>> >> +starting at 1. The number of data lanes should represent the number of
>>>>> >> +usable lanes for the hardware block. That may be limited by either the SoC or
>>>>> >> +how the platform presents the interface, and the lower value must be used.
>>>>> >> +
>>>>> >> +Lane reordering is not supported on the clock lane either, so the optional
>>>>> >> +property "clock-lane" will implicitly be <0>.
>>>>> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>>>> >> +implicitly be <0 0 0 0 0>.
>>>>> >> +Neither of these values will be checked.
>>>>> >> +
>>>>> >> +Example:
>>>>> >> + csi1: csi1@7e801000 {
>>>>> >> + compatible = "brcm,bcm2835-unicam";
>>>>> >> + reg = <0x7e801000 0x800>,
>>>>> >> + <0x7e802004 0x4>;
>>>>> >
>>>>> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>>>>>
>>>>> CMI (Clock Manager Image) consists of a total of 4 registers.
>>>>> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
>>>>> inversion of the clock and data lanes (2 data lanes available on
>>>>> CAM0).
>>>>> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
>>>>> inversion of the clock and data lanes (4 data lanes available on
>>>>> CAM1).
>>>>> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
>>>>> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
>>>>> default value is the required value. Nothing touches it that I can
>>>>> find.
>>>>>
>>>>> The range listed only covers the one register associated with that
>>>>> Unicam instance, so no other users. The other two aren't touched.
>>>>> Do 16 active register bits solely for camera clock gating really
>>>>> warrant a full clock driver?
>>>>
>>>> You should describe all the registers in DT, not just what the driver
>>>> (currently) uses.
>>>
>>> I'm not clear what you're asking for here.
>>>
>>> This binding is for the Unicam block, not for CMI (Clock Manager
>>> Imaging). In order for a Unicam instance to work, it needs to enable
>>> the relevant clock gating via 1 CMI register, and it will only ever be
>>> one register.
>>
>> Rob, the CMI just a small bit of glue required by the crossing of a
>> power domain in a unicam instance, and the two unicam instances in this
>> HW have their CMI regs next to each other. It's not really a separate
>> block, and I think describing the unicam's CMI reg in the unicam binding
>> is appropriate.
>>
>> What would you need from Dave to ack this binding?
>
> Sorry, had started to reply on this and got distracted.
>
> I guess since there seems to be only 1 other bit that could possibly
> be used (CMI_USBCTL) it is fine like this. However, my concern would
> be if the CMI registers are integrated in a different way in some
> future chip that has the same unicam instances. Or just if the
> register bits are rearranged. Those are not an uncommon occurrence.
> You would have to provide access to those registers in some other way.
> It can be dealt with, but then you have to support both cases in the
> driver. If you all are fine with that, then:
The bigisland chips match bcm2835. For capri the lane enables are
shifted down by two and the clock is up at bit 20. That would be
trivial to handle based on the compatible string, except that we can't
talk to capri's hardware from ARM anyway :(
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
2017-11-21 20:54 ` Eric Anholt
(?)
@ 2017-11-22 16:39 ` Dave Stevenson
-1 siblings, 0 replies; 30+ messages in thread
From: Dave Stevenson @ 2017-11-22 16:39 UTC (permalink / raw)
To: Eric Anholt
Cc: Rob Herring, Stefan Wahren, Mauro Carvalho Chehab, linux-media,
Sakari Ailus, Hans Verkuil,
moderated list:BROADCOM BCM2835 ARM ARCHITECTURE, devicetree
On 21 November 2017 at 20:54, Eric Anholt <eric@anholt.net> wrote:
> Rob Herring <robh@kernel.org> writes:
>
>> On Tue, Nov 21, 2017 at 1:26 PM, Eric Anholt <eric@anholt.net> wrote:
>>> Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
>>>
>>>> Hi Rob
>>>>
>>>> On 27 September 2017 at 22:51, Rob Herring <robh@kernel.org> wrote:
>>>>> On Fri, Sep 22, 2017 at 05:07:22PM +0100, Dave Stevenson wrote:
>>>>>> Hi Stefan
>>>>>>
>>>>>> On 22 September 2017 at 07:45, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>>>>>> > Hi Dave,
>>>>>> >
>>>>>> >> Dave Stevenson <dave.stevenson@raspberrypi.org> hat am 20. September 2017 um 18:07 geschrieben:
>>>>>> >>
>>>>>> >>
>>>>>> >> 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>
>>>>>> >> ---
>>>>>> >>
>>>>>> >> Changes since v2
>>>>>> >> - Removed all references to Linux drivers.
>>>>>> >> - Reworded section about disabling the firmware driver.
>>>>>> >> - Renamed clock from "lp_clock" to "lp" in description and example.
>>>>>> >> - Referred to video-interfaces.txt and stated requirements on remote-endpoint
>>>>>> >> and data-lanes.
>>>>>> >> - Corrected typo in example from csi to csi1.
>>>>>> >> - Removed unnecessary #address-cells and #size-cells in example.
>>>>>> >> - Removed setting of status from the example.
>>>>>> >>
>>>>>> >> .../devicetree/bindings/media/bcm2835-unicam.txt | 85 ++++++++++++++++++++++
>>>>>> >> 1 file changed, 85 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..7714fb3
>>>>>> >> --- /dev/null
>>>>>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>>>>> >> @@ -0,0 +1,85 @@
>>>>>> >> +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.
>>>>>> >> +
>>>>>> >> +The main platform using this SoC is the Raspberry Pi family of boards.
>>>>>> >> +On the Pi the VideoCore firmware can also control this hardware block,
>>>>>> >> +and driving it from two different processors will cause issues.
>>>>>> >> +To avoid this, the firmware checks the device tree configuration
>>>>>> >> +during boot. If it finds device tree nodes called csi0 or csi1 then
>>>>>> >> +it will stop the firmware accessing the block, and it can then
>>>>>> >> +safely be used via the device tree binding.
>>>>>> >> +
>>>>>> >> +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" entry, matching entries in the
>>>>>> >> + clocks property.
>>>>>> >> +
>>>>>> >> +Unicam supports a single port node. It should contain one 'port' child node
>>>>>> >> +with child 'endpoint' node. Please refer to the bindings defined in
>>>>>> >> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>>>>>> >> +
>>>>>> >> +Within the endpoint node the "remote-endpoint" and "data-lanes" properties
>>>>>> >> +are mandatory.
>>>>>> >> +Data lane reordering is not supported so the data lanes must be in order,
>>>>>> >> +starting at 1. The number of data lanes should represent the number of
>>>>>> >> +usable lanes for the hardware block. That may be limited by either the SoC or
>>>>>> >> +how the platform presents the interface, and the lower value must be used.
>>>>>> >> +
>>>>>> >> +Lane reordering is not supported on the clock lane either, so the optional
>>>>>> >> +property "clock-lane" will implicitly be <0>.
>>>>>> >> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>>>>>> >> +implicitly be <0 0 0 0 0>.
>>>>>> >> +Neither of these values will be checked.
>>>>>> >> +
>>>>>> >> +Example:
>>>>>> >> + csi1: csi1@7e801000 {
>>>>>> >> + compatible = "brcm,bcm2835-unicam";
>>>>>> >> + reg = <0x7e801000 0x800>,
>>>>>> >> + <0x7e802004 0x4>;
>>>>>> >
>>>>>> > sorry, i didn't noticed this before. I'm afraid this is using a small range of the CMI. Are there possible other users of this range? Does it make sense to handle this by a separate clock driver?
>>>>>>
>>>>>> CMI (Clock Manager Image) consists of a total of 4 registers.
>>>>>> 0x7e802000 is CMI_CAM0, with only bits 0-5 used for gating and
>>>>>> inversion of the clock and data lanes (2 data lanes available on
>>>>>> CAM0).
>>>>>> 0x7e802004 is CMI_CAM1, with only bits 0-9 used for gating and
>>>>>> inversion of the clock and data lanes (4 data lanes available on
>>>>>> CAM1).
>>>>>> 0x7e802008 is CMI_CAMTEST which I have no documentation or drivers for.
>>>>>> 0x7e802010 is CMI_USBCTL. Only bit 6 is documented and is a reset. The
>>>>>> default value is the required value. Nothing touches it that I can
>>>>>> find.
>>>>>>
>>>>>> The range listed only covers the one register associated with that
>>>>>> Unicam instance, so no other users. The other two aren't touched.
>>>>>> Do 16 active register bits solely for camera clock gating really
>>>>>> warrant a full clock driver?
>>>>>
>>>>> You should describe all the registers in DT, not just what the driver
>>>>> (currently) uses.
>>>>
>>>> I'm not clear what you're asking for here.
>>>>
>>>> This binding is for the Unicam block, not for CMI (Clock Manager
>>>> Imaging). In order for a Unicam instance to work, it needs to enable
>>>> the relevant clock gating via 1 CMI register, and it will only ever be
>>>> one register.
>>>
>>> Rob, the CMI just a small bit of glue required by the crossing of a
>>> power domain in a unicam instance, and the two unicam instances in this
>>> HW have their CMI regs next to each other. It's not really a separate
>>> block, and I think describing the unicam's CMI reg in the unicam binding
>>> is appropriate.
>>>
>>> What would you need from Dave to ack this binding?
>>
>> Sorry, had started to reply on this and got distracted.
>>
>> I guess since there seems to be only 1 other bit that could possibly
>> be used (CMI_USBCTL) it is fine like this. However, my concern would
>> be if the CMI registers are integrated in a different way in some
>> future chip that has the same unicam instances. Or just if the
>> register bits are rearranged. Those are not an uncommon occurrence.
>> You would have to provide access to those registers in some other way.
>> It can be dealt with, but then you have to support both cases in the
>> driver. If you all are fine with that, then:
>
> The bigisland chips match bcm2835. For capri the lane enables are
> shifted down by two and the clock is up at bit 20. That would be
> trivial to handle based on the compatible string, except that we can't
> talk to capri's hardware from ARM anyway :(
Thank you both.
The Java and Hawaii chips also have the same Unicam block, but appear
to be missing CMI totally based on the BCM Android kernel source.
They aren't chips I have any interest in, but as Eric says it can be
supported easily via the compatible string, or by making the resource
optional. The latter is easy to do, so I'll add that to v4 of the
patch set.
Cheers,
Dave
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2017-11-22 16:39 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-20 16:07 [PATCH v3 0/4] BCM283x Camera Receiver driver Dave Stevenson
[not found] ` <cover.1505916622.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
2017-09-20 16:07 ` [PATCH v3 1/4] [media] v4l2-common: Add helper function for fourcc to string Dave Stevenson
2017-09-20 16:07 ` Dave Stevenson
2017-10-13 21:09 ` Sakari Ailus
2017-10-17 9:17 ` Dave Stevenson
[not found] ` <CAAoAYcN441pVUqCu00hbKmEQWyNaK4jdwkufpJ2P8iXkcQG5KA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-10-17 9:28 ` Hans Verkuil
2017-10-17 9:28 ` Hans Verkuil
[not found] ` <8cd44cbf-6751-c3de-1729-c8c1d54e8b00-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>
2017-10-17 10:08 ` Sakari Ailus
2017-10-17 10:08 ` Sakari Ailus
2017-09-20 16:07 ` [PATCH v3 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
[not found] ` <fae3d29bba67825030c0077dd9c79534b6888512.1505916622.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
2017-09-22 6:45 ` Stefan Wahren
2017-09-22 6:45 ` Stefan Wahren
[not found] ` <1814950930.414004.1506062733728-7tX72C7vayboQLBSYMtkGA@public.gmane.org>
2017-09-22 16:07 ` Dave Stevenson
2017-09-22 16:07 ` Dave Stevenson
[not found] ` <CAAoAYcMFm82vo5k-iCCpARbndyrLDt1UMV_kRUDHiHA0iMzhMg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-09-22 17:39 ` Stefan Wahren
2017-09-22 17:39 ` Stefan Wahren
2017-09-22 18:04 ` Eric Anholt
2017-09-22 18:04 ` Eric Anholt
2017-09-27 21:51 ` Rob Herring
2017-09-27 21:51 ` Rob Herring
2017-10-02 10:36 ` Dave Stevenson
[not found] ` <CAAoAYcM0m6Z8hUDn+FuNb-O28geAYJqHWrhKPDP_Jvh2P-YE3A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-11-21 19:26 ` Eric Anholt
2017-11-21 19:26 ` Eric Anholt
2017-11-21 19:58 ` Rob Herring
2017-11-21 20:54 ` Eric Anholt
2017-11-21 20:54 ` Eric Anholt
2017-11-22 16:39 ` Dave Stevenson
2017-09-20 16:07 ` [PATCH v3 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
2017-09-22 10:37 ` Hans Verkuil
2017-09-20 16:07 ` [PATCH v3 4/4] MAINTAINERS: Add entry for BCM2835 camera driver Dave Stevenson
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.