diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0672-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0672-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch | 2255 |
1 files changed, 0 insertions, 2255 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0672-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch b/target/linux/bcm27xx/patches-5.4/950-0672-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch deleted file mode 100644 index 38015cc98a..0000000000 --- a/target/linux/bcm27xx/patches-5.4/950-0672-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch +++ /dev/null @@ -1,2255 +0,0 @@ -From 05a5bc2bfa028885c844ccc2263029b5db9160b4 Mon Sep 17 00:00:00 2001 -From: Naushir Patuck <naush@raspberrypi.com> -Date: Thu, 23 Apr 2020 10:17:37 +0100 -Subject: [PATCH] staging: vc04_services: ISP: Add a more complex ISP - processing component - -Driver for the BCM2835 ISP hardware block. This driver uses the MMAL -component to program the ISP hardware through the VC firmware. - -The ISP component can produce two video stream outputs, and Bayer -image statistics. This can't be encompassed in a simple V4L2 -M2M device, so create a new device that registers 4 video nodes. - -Signed-off-by: Naushir Patuck <naush@raspberrypi.com> ---- - MAINTAINERS | 9 + - drivers/staging/vc04_services/Kconfig | 1 + - drivers/staging/vc04_services/Makefile | 1 + - .../staging/vc04_services/bcm2835-isp/Kconfig | 14 + - .../vc04_services/bcm2835-isp/Makefile | 8 + - .../bcm2835-isp/bcm2835-v4l2-isp.c | 1627 +++++++++++++++++ - .../bcm2835-isp/bcm2835_isp_ctrls.h | 67 + - .../bcm2835-isp/bcm2835_isp_fmts.h | 272 +++ - .../vc04_services/vchiq-mmal/mmal-encodings.h | 4 + - .../vchiq-mmal/mmal-parameters.h | 153 +- - 10 files changed, 2155 insertions(+), 1 deletion(-) - create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Kconfig - create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Makefile - create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c - create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h - create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h - ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -3212,6 +3212,15 @@ S: Maintained - F: drivers/media/platform/bcm2835/ - F: Documentation/devicetree/bindings/media/bcm2835-unicam.txt - -+BROADCOM BCM2835 ISP DRIVER -+M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com> -+L: linux-media@vger.kernel.org -+S: Maintained -+F: drivers/staging/vc04_services/bcm2835-isp -+F: include/uapi/linux/bcm2835-isp.h -+F: Documentation/media/v4l-drivers/bcm2835-isp.rst -+F: Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst -+ - BROADCOM BCM47XX MIPS ARCHITECTURE - M: Hauke Mehrtens <hauke@hauke-m.de> - M: Rafał Miłecki <zajec5@gmail.com> ---- a/drivers/staging/vc04_services/Kconfig -+++ b/drivers/staging/vc04_services/Kconfig -@@ -25,6 +25,7 @@ source "drivers/staging/vc04_services/bc - source "drivers/staging/vc04_services/vchiq-mmal/Kconfig" - source "drivers/staging/vc04_services/vc-sm-cma/Kconfig" - source "drivers/staging/vc04_services/bcm2835-codec/Kconfig" -+source "drivers/staging/vc04_services/bcm2835-isp/Kconfig" - - endif - ---- a/drivers/staging/vc04_services/Makefile -+++ b/drivers/staging/vc04_services/Makefile -@@ -15,6 +15,7 @@ obj-$(CONFIG_VIDEO_BCM2835) += bcm2835- - obj-$(CONFIG_BCM2835_VCHIQ_MMAL) += vchiq-mmal/ - obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/ - obj-$(CONFIG_VIDEO_CODEC_BCM2835) += bcm2835-codec/ -+obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp/ - - ccflags-y += -Idrivers/staging/vc04_services -D__VCCOREVER__=0x04000000 - ---- /dev/null -+++ b/drivers/staging/vc04_services/bcm2835-isp/Kconfig -@@ -0,0 +1,14 @@ -+config VIDEO_ISP_BCM2835 -+ tristate "BCM2835 ISP support" -+ depends on MEDIA_SUPPORT -+ depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST) -+ depends on MEDIA_CONTROLLER -+ select BCM2835_VCHIQ_MMAL -+ select VIDEOBUF2_DMA_CONTIG -+ help -+ This is the V4L2 driver for the Broadcom BCM2835 ISP hardware. -+ This operates over the VCHIQ interface to a service running on -+ VideoCore. -+ -+ To compile this driver as a module, choose M here: the module -+ will be called bcm2835-isp. ---- /dev/null -+++ b/drivers/staging/vc04_services/bcm2835-isp/Makefile -@@ -0,0 +1,8 @@ -+# SPDX-License-Identifier: GPL-2.0 -+bcm2835-isp-objs := bcm2835-v4l2-isp.o -+ -+obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp.o -+ -+ccflags-y += \ -+ -I$(srctree)/drivers/staging/vc04_services \ -+ -D__VCCOREVER__=0x04000000 ---- /dev/null -+++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c -@@ -0,0 +1,1627 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Broadcom BCM2835 ISP driver -+ * -+ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. -+ * -+ * Author: Naushir Patuck (naush@raspberrypi.com) -+ * -+ */ -+ -+#include <linux/module.h> -+#include <linux/platform_device.h> -+ -+#include <media/v4l2-ctrls.h> -+#include <media/v4l2-device.h> -+#include <media/v4l2-event.h> -+#include <media/v4l2-ioctl.h> -+#include <media/videobuf2-dma-contig.h> -+ -+#include "vchiq-mmal/mmal-msg.h" -+#include "vchiq-mmal/mmal-parameters.h" -+#include "vchiq-mmal/mmal-vchiq.h" -+ -+#include "bcm2835_isp_ctrls.h" -+#include "bcm2835_isp_fmts.h" -+ -+static unsigned int debug; -+module_param(debug, uint, 0644); -+MODULE_PARM_DESC(debug, "activates debug info"); -+ -+static unsigned int video_nr = 13; -+module_param(video_nr, uint, 0644); -+MODULE_PARM_DESC(video_nr, "base video device number"); -+ -+#define BCM2835_ISP_NAME "bcm2835-isp" -+#define BCM2835_ISP_ENTITY_NAME_LEN 32 -+ -+#define BCM2835_ISP_NUM_OUTPUTS 1 -+#define BCM2835_ISP_NUM_CAPTURES 2 -+#define BCM2835_ISP_NUM_METADATA 1 -+ -+#define BCM2835_ISP_NUM_NODES \ -+ (BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES + \ -+ BCM2835_ISP_NUM_METADATA) -+ -+/* Default frame dimension of 1280 pixels. */ -+#define DEFAULT_DIM 1280U -+/* -+ * Maximum frame dimension of 16384 pixels. Even though the ISP runs in tiles, -+ * have a sensible limit so that we do not create an excessive number of tiles -+ * to process. -+ */ -+#define MAX_DIM 16384U -+/* -+ * Minimum frame dimension of 64 pixels. Anything lower, and the tiling -+ * algorihtm may not be able to cope when applying filter context. -+ */ -+#define MIN_DIM 64U -+ -+/* Per-queue, driver-specific private data */ -+struct bcm2835_isp_q_data { -+ /* -+ * These parameters should be treated as gospel, with everything else -+ * being determined from them. -+ */ -+ unsigned int bytesperline; -+ unsigned int width; -+ unsigned int height; -+ unsigned int sizeimage; -+ struct bcm2835_isp_fmt *fmt; -+}; -+ -+/* -+ * Structure to describe a single node /dev/video<N> which represents a single -+ * input or output queue to the ISP device. -+ */ -+struct bcm2835_isp_node { -+ int vfl_dir; -+ unsigned int id; -+ const char *name; -+ struct video_device vfd; -+ struct media_pad pad; -+ struct media_intf_devnode *intf_devnode; -+ struct media_link *intf_link; -+ struct mutex lock; /* top level device node lock */ -+ struct mutex queue_lock; -+ -+ struct vb2_queue queue; -+ unsigned int sequence; -+ -+ /* The list of formats supported on the node. */ -+ struct bcm2835_isp_fmt_list supported_fmts; -+ -+ struct bcm2835_isp_q_data q_data; -+ -+ /* Parent device structure */ -+ struct bcm2835_isp_dev *dev; -+ -+ bool registered; -+ bool media_node_registered; -+ bool queue_init; -+}; -+ -+/* -+ * Structure representing the entire ISP device, comprising several input and -+ * output nodes /dev/video<N>. -+ */ -+struct bcm2835_isp_dev { -+ struct v4l2_device v4l2_dev; -+ struct device *dev; -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct media_device mdev; -+ struct media_entity entity; -+ bool media_device_registered; -+ bool media_entity_registered; -+ struct vchiq_mmal_instance *mmal_instance; -+ struct vchiq_mmal_component *component; -+ struct completion frame_cmplt; -+ -+ struct bcm2835_isp_node node[BCM2835_ISP_NUM_NODES]; -+ struct media_pad pad[BCM2835_ISP_NUM_NODES]; -+ atomic_t num_streaming; -+ -+ /* Image pipeline controls. */ -+ int r_gain; -+ int b_gain; -+}; -+ -+struct bcm2835_isp_buffer { -+ struct vb2_v4l2_buffer vb; -+ struct mmal_buffer mmal; -+}; -+ -+static -+inline struct bcm2835_isp_dev *node_get_dev(struct bcm2835_isp_node *node) -+{ -+ return node->dev; -+} -+ -+static inline bool node_is_output(struct bcm2835_isp_node *node) -+{ -+ return node->queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT; -+} -+ -+static inline bool node_is_capture(struct bcm2835_isp_node *node) -+{ -+ return node->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE; -+} -+ -+static inline bool node_is_stats(struct bcm2835_isp_node *node) -+{ -+ return node->queue.type == V4L2_BUF_TYPE_META_CAPTURE; -+} -+ -+static inline enum v4l2_buf_type index_to_queue_type(int index) -+{ -+ if (index < BCM2835_ISP_NUM_OUTPUTS) -+ return V4L2_BUF_TYPE_VIDEO_OUTPUT; -+ else if (index < BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES) -+ return V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ else -+ return V4L2_BUF_TYPE_META_CAPTURE; -+} -+ -+static struct vchiq_mmal_port *get_port_data(struct bcm2835_isp_node *node) -+{ -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ -+ if (!dev->component) -+ return NULL; -+ -+ switch (node->queue.type) { -+ case V4L2_BUF_TYPE_VIDEO_OUTPUT: -+ return &dev->component->input[node->id]; -+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: -+ case V4L2_BUF_TYPE_META_CAPTURE: -+ return &dev->component->output[node->id]; -+ default: -+ v4l2_err(&dev->v4l2_dev, "%s: Invalid queue type %u\n", -+ __func__, node->queue.type); -+ break; -+ } -+ return NULL; -+} -+ -+static int set_isp_param(struct bcm2835_isp_node *node, u32 parameter, -+ void *value, u32 value_size) -+{ -+ struct vchiq_mmal_port *port = get_port_data(node); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ -+ return vchiq_mmal_port_parameter_set(dev->mmal_instance, port, -+ parameter, value, value_size); -+} -+ -+static int set_wb_gains(struct bcm2835_isp_node *node) -+{ -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct mmal_parameter_awbgains gains = { -+ .r_gain = { dev->r_gain, 1000 }, -+ .b_gain = { dev->b_gain, 1000 } -+ }; -+ -+ return set_isp_param(node, MMAL_PARAMETER_CUSTOM_AWB_GAINS, -+ &gains, sizeof(gains)); -+} -+ -+static int set_digital_gain(struct bcm2835_isp_node *node, uint32_t gain) -+{ -+ struct mmal_parameter_rational digital_gain = { -+ .num = gain, -+ .den = 1000 -+ }; -+ -+ return set_isp_param(node, MMAL_PARAMETER_DIGITAL_GAIN, -+ &digital_gain, sizeof(digital_gain)); -+} -+ -+static const struct bcm2835_isp_fmt *get_fmt(u32 mmal_fmt) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { -+ if (supported_formats[i].mmal_fmt == mmal_fmt) -+ return &supported_formats[i]; -+ } -+ return NULL; -+} -+ -+static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, -+ struct bcm2835_isp_node *node) -+{ -+ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; -+ struct bcm2835_isp_fmt *fmt; -+ unsigned int i; -+ -+ for (i = 0; i < fmts->num_entries; i++) { -+ fmt = &fmts->list[i]; -+ if (fmt->fourcc == (node_is_stats(node) ? -+ f->fmt.meta.dataformat : -+ f->fmt.pix.pixelformat)) -+ return fmt; -+ } -+ -+ return NULL; -+} -+ -+/* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL -+ * -+ * Copies all the required fields from a VB2 buffer to the MMAL buffer header, -+ * ready for sending to the VPU. -+ */ -+static void vb2_to_mmal_buffer(struct mmal_buffer *buf, -+ struct vb2_v4l2_buffer *vb2) -+{ -+ u64 pts; -+ -+ buf->mmal_flags = 0; -+ if (vb2->flags & V4L2_BUF_FLAG_KEYFRAME) -+ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME; -+ -+ /* Data must be framed correctly as one frame per buffer. */ -+ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; -+ -+ buf->length = vb2->vb2_buf.planes[0].bytesused; -+ /* -+ * Minor ambiguity in the V4L2 spec as to whether passing in a 0 length -+ * buffer, or one with V4L2_BUF_FLAG_LAST set denotes end of stream. -+ * Handle either. -+ */ -+ if (!buf->length || vb2->flags & V4L2_BUF_FLAG_LAST) -+ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_EOS; -+ -+ /* vb2 timestamps in nsecs, mmal in usecs */ -+ pts = vb2->vb2_buf.timestamp; -+ do_div(pts, 1000); -+ buf->pts = pts; -+ buf->dts = MMAL_TIME_UNKNOWN; -+} -+ -+static void mmal_buffer_cb(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, int status, -+ struct mmal_buffer *mmal_buf) -+{ -+ struct bcm2835_isp_buffer *q_buf; -+ struct bcm2835_isp_node *node = port->cb_ctx; -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct vb2_v4l2_buffer *vb2; -+ -+ q_buf = container_of(mmal_buf, struct bcm2835_isp_buffer, mmal); -+ vb2 = &q_buf->vb; -+ v4l2_dbg(2, debug, &dev->v4l2_dev, -+ "%s: port:%s[%d], status:%d, buf:%p, dmabuf:%p, length:%lu, flags %u, pts %lld\n", -+ __func__, node_is_output(node) ? "input" : "output", node->id, -+ status, mmal_buf, mmal_buf->dma_buf, mmal_buf->length, -+ mmal_buf->mmal_flags, mmal_buf->pts); -+ -+ if (mmal_buf->cmd) -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Unexpected event on output callback - %08x\n", -+ __func__, mmal_buf->cmd); -+ -+ if (status) { -+ /* error in transfer */ -+ if (vb2) { -+ /* there was a buffer with the error so return it */ -+ vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_ERROR); -+ } -+ return; -+ } -+ -+ /* vb2 timestamps in nsecs, mmal in usecs */ -+ vb2->vb2_buf.timestamp = mmal_buf->pts * 1000; -+ vb2->sequence = node->sequence++; -+ vb2_set_plane_payload(&vb2->vb2_buf, 0, mmal_buf->length); -+ vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_DONE); -+ -+ if (!port->enabled) -+ complete(&dev->frame_cmplt); -+} -+ -+static void setup_mmal_port_format(struct bcm2835_isp_node *node, -+ struct vchiq_mmal_port *port) -+{ -+ struct bcm2835_isp_q_data *q_data = &node->q_data; -+ -+ port->format.encoding = q_data->fmt->mmal_fmt; -+ /* Raw image format - set width/height */ -+ port->es.video.width = (q_data->bytesperline << 3) / q_data->fmt->depth; -+ port->es.video.height = q_data->height; -+ port->es.video.crop.width = q_data->width; -+ port->es.video.crop.height = q_data->height; -+ port->es.video.crop.x = 0; -+ port->es.video.crop.y = 0; -+}; -+ -+static int setup_mmal_port(struct bcm2835_isp_node *node) -+{ -+ struct vchiq_mmal_port *port = get_port_data(node); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ unsigned int enable = 1; -+ int ret; -+ -+ v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: setup %s[%d]\n", __func__, -+ node->name, node->id); -+ -+ vchiq_mmal_port_parameter_set(dev->mmal_instance, port, -+ MMAL_PARAMETER_ZERO_COPY, &enable, -+ sizeof(enable)); -+ setup_mmal_port_format(node, port); -+ ret = vchiq_mmal_port_set_format(dev->mmal_instance, port); -+ if (ret < 0) { -+ v4l2_dbg(1, debug, &dev->v4l2_dev, -+ "%s: vchiq_mmal_port_set_format failed\n", -+ __func__); -+ return ret; -+ } -+ -+ if (node->q_data.sizeimage < port->minimum_buffer.size) { -+ v4l2_err(&dev->v4l2_dev, -+ "buffer size mismatch sizeimage %u < min size %u\n", -+ node->q_data.sizeimage, port->minimum_buffer.size); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int bcm2835_isp_mmal_buf_cleanup(struct mmal_buffer *mmal_buf) -+{ -+ mmal_vchi_buffer_cleanup(mmal_buf); -+ -+ if (mmal_buf->dma_buf) { -+ dma_buf_put(mmal_buf->dma_buf); -+ mmal_buf->dma_buf = NULL; -+ } -+ -+ return 0; -+} -+ -+static int bcm2835_isp_node_queue_setup(struct vb2_queue *q, -+ unsigned int *nbuffers, -+ unsigned int *nplanes, -+ unsigned int sizes[], -+ struct device *alloc_devs[]) -+{ -+ struct bcm2835_isp_node *node = vb2_get_drv_priv(q); -+ struct vchiq_mmal_port *port; -+ unsigned int size; -+ -+ if (setup_mmal_port(node)) -+ return -EINVAL; -+ -+ size = node->q_data.sizeimage; -+ if (size == 0) { -+ v4l2_info(&node_get_dev(node)->v4l2_dev, -+ "%s: Image size unset in queue_setup for node %s[%d]\n", -+ __func__, node->name, node->id); -+ return -EINVAL; -+ } -+ -+ if (*nplanes) -+ return sizes[0] < size ? -EINVAL : 0; -+ -+ *nplanes = 1; -+ sizes[0] = size; -+ -+ port = get_port_data(node); -+ port->current_buffer.size = size; -+ -+ if (*nbuffers < port->minimum_buffer.num) -+ *nbuffers = port->minimum_buffer.num; -+ -+ port->current_buffer.num = *nbuffers; -+ -+ v4l2_dbg(2, debug, &node_get_dev(node)->v4l2_dev, -+ "%s: Image size %u, nbuffers %u for node %s[%d]\n", -+ __func__, sizes[0], *nbuffers, node->name, node->id); -+ return 0; -+} -+ -+static int bcm2835_isp_buf_init(struct vb2_buffer *vb) -+{ -+ struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); -+ struct bcm2835_isp_buffer *buf = -+ container_of(vb2, struct bcm2835_isp_buffer, vb); -+ -+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: vb %p\n", __func__, vb); -+ -+ buf->mmal.buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); -+ buf->mmal.buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 0); -+ mmal_vchi_buffer_init(dev->mmal_instance, &buf->mmal); -+ return 0; -+} -+ -+static int bcm2835_isp_buf_prepare(struct vb2_buffer *vb) -+{ -+ struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); -+ struct bcm2835_isp_buffer *buf = -+ container_of(vb2, struct bcm2835_isp_buffer, vb); -+ struct dma_buf *dma_buf; -+ int ret; -+ -+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: type: %d ptr %p\n", -+ __func__, vb->vb2_queue->type, vb); -+ -+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { -+ if (vb2->field == V4L2_FIELD_ANY) -+ vb2->field = V4L2_FIELD_NONE; -+ if (vb2->field != V4L2_FIELD_NONE) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s field isn't supported\n", __func__); -+ return -EINVAL; -+ } -+ } -+ -+ if (vb2_plane_size(vb, 0) < node->q_data.sizeimage) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s data will not fit into plane (%lu < %lu)\n", -+ __func__, vb2_plane_size(vb, 0), -+ (long)node->q_data.sizeimage); -+ return -EINVAL; -+ } -+ -+ if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) -+ vb2_set_plane_payload(vb, 0, node->q_data.sizeimage); -+ -+ switch (vb->memory) { -+ case VB2_MEMORY_DMABUF: -+ dma_buf = dma_buf_get(vb->planes[0].m.fd); -+ -+ if (dma_buf != buf->mmal.dma_buf) { -+ /* -+ * dmabuf either hasn't already been mapped, or it has -+ * changed. -+ */ -+ if (buf->mmal.dma_buf) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s Buffer changed - why did the core not call cleanup?\n", -+ __func__); -+ bcm2835_isp_mmal_buf_cleanup(&buf->mmal); -+ } -+ -+ buf->mmal.dma_buf = dma_buf; -+ } else { -+ /* -+ * Already have a reference to the buffer, so release it -+ * here. -+ */ -+ dma_buf_put(dma_buf); -+ } -+ ret = 0; -+ break; -+ case VB2_MEMORY_MMAP: -+ /* -+ * We want to do this at init, but vb2_core_expbuf checks that -+ * the index < q->num_buffers, and q->num_buffers only gets -+ * updated once all the buffers are allocated. -+ */ -+ if (!buf->mmal.dma_buf) { -+ ret = vb2_core_expbuf_dmabuf(vb->vb2_queue, -+ vb->vb2_queue->type, -+ vb->index, 0, O_CLOEXEC, -+ &buf->mmal.dma_buf); -+ v4l2_dbg(3, debug, &dev->v4l2_dev, -+ "%s: exporting ptr %p to dmabuf %p\n", -+ __func__, vb, buf->mmal.dma_buf); -+ if (ret) -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Failed to expbuf idx %d, ret %d\n", -+ __func__, vb->index, ret); -+ } else { -+ ret = 0; -+ } -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ return ret; -+} -+ -+static void bcm2835_isp_node_buffer_queue(struct vb2_buffer *buf) -+{ -+ struct bcm2835_isp_node *node = vb2_get_drv_priv(buf->vb2_queue); -+ struct vb2_v4l2_buffer *vbuf = -+ container_of(buf, struct vb2_v4l2_buffer, vb2_buf); -+ struct bcm2835_isp_buffer *buffer = -+ container_of(vbuf, struct bcm2835_isp_buffer, vb); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ -+ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: node %s[%d], buffer %p\n", -+ __func__, node->name, node->id, buffer); -+ -+ vb2_to_mmal_buffer(&buffer->mmal, &buffer->vb); -+ v4l2_dbg(3, debug, &dev->v4l2_dev, -+ "%s: node %s[%d] - submitting mmal dmabuf %p\n", __func__, -+ node->name, node->id, buffer->mmal.dma_buf); -+ vchiq_mmal_submit_buffer(dev->mmal_instance, get_port_data(node), -+ &buffer->mmal); -+} -+ -+static void bcm2835_isp_buffer_cleanup(struct vb2_buffer *vb) -+{ -+ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); -+ struct bcm2835_isp_buffer *buffer = -+ container_of(vb2, struct bcm2835_isp_buffer, vb); -+ -+ bcm2835_isp_mmal_buf_cleanup(&buffer->mmal); -+} -+ -+static int bcm2835_isp_node_start_streaming(struct vb2_queue *q, -+ unsigned int count) -+{ -+ struct bcm2835_isp_node *node = vb2_get_drv_priv(q); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct vchiq_mmal_port *port = get_port_data(node); -+ int ret; -+ -+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d] (count %u)\n", -+ __func__, node->name, node->id, count); -+ -+ ret = vchiq_mmal_component_enable(dev->mmal_instance, dev->component); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, "%s: Failed enabling component, ret %d\n", -+ __func__, ret); -+ return -EIO; -+ } -+ -+ node->sequence = 0; -+ port->cb_ctx = node; -+ ret = vchiq_mmal_port_enable(dev->mmal_instance, port, -+ mmal_buffer_cb); -+ if (!ret) -+ atomic_inc(&dev->num_streaming); -+ else -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Failed enabling port, ret %d\n", __func__, ret); -+ -+ return ret; -+} -+ -+static void bcm2835_isp_node_stop_streaming(struct vb2_queue *q) -+{ -+ struct bcm2835_isp_node *node = vb2_get_drv_priv(q); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct vchiq_mmal_port *port = get_port_data(node); -+ unsigned int i; -+ int ret; -+ -+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d], mmal port %p\n", -+ __func__, node->name, node->id, port); -+ -+ init_completion(&dev->frame_cmplt); -+ -+ /* Disable MMAL port - this will flush buffers back */ -+ ret = vchiq_mmal_port_disable(dev->mmal_instance, port); -+ if (ret) -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Failed disabling %s port, ret %d\n", __func__, -+ node_is_output(node) ? "i/p" : "o/p", -+ ret); -+ -+ while (atomic_read(&port->buffers_with_vpu)) { -+ v4l2_dbg(1, debug, &dev->v4l2_dev, -+ "%s: Waiting for buffers to be returned - %d outstanding\n", -+ __func__, atomic_read(&port->buffers_with_vpu)); -+ ret = wait_for_completion_timeout(&dev->frame_cmplt, HZ); -+ if (ret <= 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Timeout waiting for buffers to be returned - %d outstanding\n", -+ __func__, -+ atomic_read(&port->buffers_with_vpu)); -+ break; -+ } -+ } -+ -+ /* Release the VCSM handle here to release the associated dmabuf */ -+ for (i = 0; i < q->num_buffers; i++) { -+ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(q->bufs[i]); -+ struct bcm2835_isp_buffer *buf = -+ container_of(vb2, struct bcm2835_isp_buffer, vb); -+ bcm2835_isp_mmal_buf_cleanup(&buf->mmal); -+ } -+ -+ atomic_dec(&dev->num_streaming); -+ /* If all ports disabled, then disable the component */ -+ if (atomic_read(&dev->num_streaming) == 0) { -+ ret = vchiq_mmal_component_disable(dev->mmal_instance, -+ dev->component); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Failed disabling component, ret %d\n", -+ __func__, ret); -+ } -+ } -+ -+ /* -+ * Simply wait for any vb2 buffers to finish. We could take steps to -+ * make them complete more quickly if we care, or even return them -+ * ourselves. -+ */ -+ vb2_wait_for_all_buffers(&node->queue); -+} -+ -+static const struct vb2_ops bcm2835_isp_node_queue_ops = { -+ .queue_setup = bcm2835_isp_node_queue_setup, -+ .buf_init = bcm2835_isp_buf_init, -+ .buf_prepare = bcm2835_isp_buf_prepare, -+ .buf_queue = bcm2835_isp_node_buffer_queue, -+ .buf_cleanup = bcm2835_isp_buffer_cleanup, -+ .start_streaming = bcm2835_isp_node_start_streaming, -+ .stop_streaming = bcm2835_isp_node_stop_streaming, -+}; -+ -+static struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node) -+{ -+ return &node->supported_fmts.list[0]; -+} -+ -+static inline unsigned int get_bytesperline(int width, -+ struct bcm2835_isp_fmt *fmt) -+{ -+ return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align); -+} -+ -+static inline unsigned int get_sizeimage(int bpl, int width, int height, -+ struct bcm2835_isp_fmt *fmt) -+{ -+ return (bpl * height * fmt->size_multiplier_x2) >> 1; -+} -+ -+static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct bcm2835_isp_dev *dev = -+ container_of(ctrl->handler, struct bcm2835_isp_dev, ctrl_handler); -+ struct bcm2835_isp_node *node = &dev->node[0]; -+ int ret = 0; -+ -+ /* -+ * The ISP firmware driver will ensure these settings are applied on -+ * a frame boundary, so we are safe to write them as they come in. -+ * -+ * Note that the bcm2835_isp_* param structures are identical to the -+ * mmal-parameters.h definitions. This avoids the need for unnecessary -+ * field-by-field copying between structures. -+ */ -+ switch (ctrl->id) { -+ case V4L2_CID_RED_BALANCE: -+ dev->r_gain = ctrl->val; -+ ret = set_wb_gains(node); -+ break; -+ case V4L2_CID_BLUE_BALANCE: -+ dev->b_gain = ctrl->val; -+ ret = set_wb_gains(node); -+ break; -+ case V4L2_CID_DIGITAL_GAIN: -+ ret = set_digital_gain(node, ctrl->val); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_CC_MATRIX: -+ ret = set_isp_param(node, MMAL_PARAMETER_CUSTOM_CCM, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_custom_ccm)); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING: -+ ret = set_isp_param(node, MMAL_PARAMETER_LENS_SHADING_OVERRIDE, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_lens_shading)); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL: -+ ret = set_isp_param(node, MMAL_PARAMETER_BLACK_LEVEL, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_black_level)); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_GEQ: -+ ret = set_isp_param(node, MMAL_PARAMETER_GEQ, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_geq)); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_GAMMA: -+ ret = set_isp_param(node, MMAL_PARAMETER_GAMMA, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_gamma)); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_DENOISE: -+ ret = set_isp_param(node, MMAL_PARAMETER_DENOISE, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_denoise)); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_SHARPEN: -+ ret = set_isp_param(node, MMAL_PARAMETER_SHARPEN, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_sharpen)); -+ break; -+ case V4L2_CID_USER_BCM2835_ISP_DPC: -+ ret = set_isp_param(node, MMAL_PARAMETER_DPC, -+ ctrl->p_new.p_u8, -+ sizeof(struct bcm2835_isp_dpc)); -+ break; -+ default: -+ v4l2_info(&dev->v4l2_dev, "Unrecognised control\n"); -+ ret = -EINVAL; -+ } -+ -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, "%s: Failed setting ctrl \"%s\" (%08x), err %d\n", -+ __func__, ctrl->name, ctrl->id, ret); -+ ret = -EIO; -+ } -+ -+ return ret; -+} -+ -+static const struct v4l2_ctrl_ops bcm2835_isp_ctrl_ops = { -+ .s_ctrl = bcm2835_isp_s_ctrl, -+}; -+ -+static const struct v4l2_file_operations bcm2835_isp_fops = { -+ .owner = THIS_MODULE, -+ .open = v4l2_fh_open, -+ .release = vb2_fop_release, -+ .poll = vb2_fop_poll, -+ .unlocked_ioctl = video_ioctl2, -+ .mmap = vb2_fop_mmap -+}; -+ -+static int populate_qdata_fmt(struct v4l2_format *f, -+ struct bcm2835_isp_node *node) -+{ -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct bcm2835_isp_q_data *q_data = &node->q_data; -+ struct vchiq_mmal_port *port; -+ int ret; -+ -+ if (!node_is_stats(node)) { -+ v4l2_dbg(1, debug, &dev->v4l2_dev, -+ "%s: Setting pix format for type %d, wxh: %ux%u, fmt: %08x, size %u\n", -+ __func__, f->type, f->fmt.pix.width, f->fmt.pix.height, -+ f->fmt.pix.pixelformat, f->fmt.pix.sizeimage); -+ -+ q_data->fmt = find_format(f, node); -+ q_data->width = f->fmt.pix.width; -+ q_data->height = f->fmt.pix.height; -+ q_data->height = f->fmt.pix.height; -+ -+ /* All parameters should have been set correctly by try_fmt */ -+ q_data->bytesperline = f->fmt.pix.bytesperline; -+ q_data->sizeimage = f->fmt.pix.sizeimage; -+ } else { -+ v4l2_dbg(1, debug, &dev->v4l2_dev, -+ "%s: Setting meta format for fmt: %08x, size %u\n", -+ __func__, f->fmt.meta.dataformat, -+ f->fmt.meta.buffersize); -+ -+ q_data->fmt = find_format(f, node); -+ q_data->width = 0; -+ q_data->height = 0; -+ q_data->bytesperline = 0; -+ q_data->sizeimage = f->fmt.meta.buffersize; -+ } -+ -+ v4l2_dbg(1, debug, &dev->v4l2_dev, -+ "%s: Calculated bpl as %u, size %u\n", __func__, -+ q_data->bytesperline, q_data->sizeimage); -+ -+ /* If we have a component then setup the port as well */ -+ port = get_port_data(node); -+ setup_mmal_port_format(node, port); -+ ret = vchiq_mmal_port_set_format(dev->mmal_instance, port); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Failed vchiq_mmal_port_set_format on port, ret %d\n", -+ __func__, ret); -+ ret = -EINVAL; -+ } -+ -+ if (q_data->sizeimage < port->minimum_buffer.size) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: Current buffer size of %u < min buf size %u - driver mismatch to MMAL\n", -+ __func__, -+ q_data->sizeimage, -+ port->minimum_buffer.size); -+ } -+ -+ v4l2_dbg(1, debug, &dev->v4l2_dev, -+ "%s: Set format for type %d, wxh: %dx%d, fmt: %08x, size %u\n", -+ __func__, f->type, q_data->width, q_data->height, -+ q_data->fmt->fourcc, q_data->sizeimage); -+ -+ return ret; -+} -+ -+static int bcm2835_isp_node_querycap(struct file *file, void *priv, -+ struct v4l2_capability *cap) -+{ -+ strscpy(cap->driver, BCM2835_ISP_NAME, sizeof(cap->driver)); -+ strscpy(cap->card, BCM2835_ISP_NAME, sizeof(cap->card)); -+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", -+ BCM2835_ISP_NAME); -+ -+ return 0; -+} -+ -+static int bcm2835_isp_node_g_fmt(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bcm2835_isp_node *node = video_drvdata(file); -+ -+ if (f->type != node->queue.type) -+ return -EINVAL; -+ -+ if (node_is_stats(node)) { -+ f->fmt.meta.dataformat = V4L2_META_FMT_BCM2835_ISP_STATS; -+ f->fmt.meta.buffersize = -+ get_port_data(node)->minimum_buffer.size; -+ } else { -+ struct bcm2835_isp_q_data *q_data = &node->q_data; -+ -+ f->fmt.pix.width = q_data->width; -+ f->fmt.pix.height = q_data->height; -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ f->fmt.pix.pixelformat = q_data->fmt->fourcc; -+ f->fmt.pix.bytesperline = q_data->bytesperline; -+ f->fmt.pix.sizeimage = q_data->sizeimage; -+ f->fmt.pix.colorspace = q_data->fmt->colorspace; -+ } -+ -+ return 0; -+} -+ -+static int bcm2835_isp_node_enum_fmt(struct file *file, void *priv, -+ struct v4l2_fmtdesc *f) -+{ -+ struct bcm2835_isp_node *node = video_drvdata(file); -+ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; -+ -+ if (f->type != node->queue.type) -+ return -EINVAL; -+ -+ if (f->index < fmts->num_entries) { -+ /* Format found */ -+ f->pixelformat = fmts->list[f->index].fourcc; -+ f->flags = fmts->list[f->index].flags; -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int bcm2835_isp_node_try_fmt(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bcm2835_isp_node *node = video_drvdata(file); -+ struct bcm2835_isp_fmt *fmt; -+ -+ if (f->type != node->queue.type) -+ return -EINVAL; -+ -+ fmt = find_format(f, node); -+ if (!fmt) -+ fmt = get_default_format(node); -+ -+ if (!node_is_stats(node)) { -+ f->fmt.pix.width = max(min(f->fmt.pix.width, MAX_DIM), -+ MIN_DIM); -+ f->fmt.pix.height = max(min(f->fmt.pix.height, MAX_DIM), -+ MIN_DIM); -+ -+ f->fmt.pix.pixelformat = fmt->fourcc; -+ f->fmt.pix.colorspace = fmt->colorspace; -+ f->fmt.pix.bytesperline = get_bytesperline(f->fmt.pix.width, -+ fmt); -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ f->fmt.pix.sizeimage = -+ get_sizeimage(f->fmt.pix.bytesperline, f->fmt.pix.width, -+ f->fmt.pix.height, fmt); -+ } else { -+ f->fmt.meta.dataformat = fmt->fourcc; -+ f->fmt.meta.buffersize = -+ get_port_data(node)->minimum_buffer.size; -+ } -+ -+ return 0; -+} -+ -+static int bcm2835_isp_node_s_fmt(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bcm2835_isp_node *node = video_drvdata(file); -+ int ret; -+ -+ if (f->type != node->queue.type) -+ return -EINVAL; -+ -+ ret = bcm2835_isp_node_try_fmt(file, priv, f); -+ if (ret) -+ return ret; -+ -+ v4l2_dbg(1, debug, &node_get_dev(node)->v4l2_dev, -+ "%s: Set format for node %s[%d]\n", -+ __func__, node->name, node->id); -+ -+ return populate_qdata_fmt(f, node); -+} -+ -+static int bcm2835_isp_node_s_selection(struct file *file, void *fh, -+ struct v4l2_selection *s) -+{ -+ struct mmal_parameter_crop crop; -+ struct bcm2835_isp_node *node = video_drvdata(file); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct vchiq_mmal_port *port = get_port_data(node); -+ -+ /* This return value is required fro V4L2 compliance. */ -+ if (node_is_stats(node)) -+ return -ENOTTY; -+ -+ if (!s->r.width || !s->r.height) -+ return -EINVAL; -+ -+ /* Adjust the crop window if goes outside the frame dimensions. */ -+ s->r.left = min((unsigned int)max(s->r.left, 0), -+ node->q_data.width - MIN_DIM); -+ s->r.top = min((unsigned int)max(s->r.top, 0), -+ node->q_data.height - MIN_DIM); -+ s->r.width = max(min(s->r.width, node->q_data.width - s->r.left), -+ MIN_DIM); -+ s->r.height = max(min(s->r.height, node->q_data.height - s->r.top), -+ MIN_DIM); -+ -+ crop.rect.x = s->r.left; -+ crop.rect.y = s->r.top; -+ crop.rect.width = s->r.width; -+ crop.rect.height = s->r.height; -+ -+ return vchiq_mmal_port_parameter_set(dev->mmal_instance, port, -+ MMAL_PARAMETER_CROP, -+ &crop, sizeof(crop)); -+} -+ -+static int bcm2835_isp_node_g_selection(struct file *file, void *fh, -+ struct v4l2_selection *s) -+{ -+ struct bcm2835_isp_node *node = video_drvdata(file); -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct vchiq_mmal_port *port = get_port_data(node); -+ struct mmal_parameter_crop crop; -+ u32 crop_size = sizeof(crop); -+ int ret; -+ -+ /* This return value is required for V4L2 compliance. */ -+ if (node_is_stats(node)) -+ return -ENOTTY; -+ -+ /* We can only return out an input crop. */ -+ if (s->target != V4L2_SEL_TGT_CROP) -+ return -EINVAL; -+ -+ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, port, -+ MMAL_PARAMETER_CROP, -+ &crop, &crop_size); -+ if (!ret) -+ return -EINVAL; -+ -+ s->r.left = crop.rect.x; -+ s->r.top = crop.rect.y; -+ s->r.width = crop.rect.width; -+ s->r.height = crop.rect.height; -+ -+ return 0; -+} -+ -+static int bcm3285_isp_subscribe_event(struct v4l2_fh *fh, -+ const struct v4l2_event_subscription *s) -+{ -+ switch (s->type) { -+ /* Cannot change source parameters dynamically at runtime. */ -+ case V4L2_EVENT_SOURCE_CHANGE: -+ return -EINVAL; -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, s); -+ default: -+ return v4l2_event_subscribe(fh, s, 4, NULL); -+ } -+} -+ -+static const struct v4l2_ioctl_ops bcm2835_isp_node_ioctl_ops = { -+ .vidioc_querycap = bcm2835_isp_node_querycap, -+ .vidioc_g_fmt_vid_cap = bcm2835_isp_node_g_fmt, -+ .vidioc_g_fmt_vid_out = bcm2835_isp_node_g_fmt, -+ .vidioc_g_fmt_meta_cap = bcm2835_isp_node_g_fmt, -+ .vidioc_s_fmt_vid_cap = bcm2835_isp_node_s_fmt, -+ .vidioc_s_fmt_vid_out = bcm2835_isp_node_s_fmt, -+ .vidioc_s_fmt_meta_cap = bcm2835_isp_node_s_fmt, -+ .vidioc_try_fmt_vid_cap = bcm2835_isp_node_try_fmt, -+ .vidioc_try_fmt_vid_out = bcm2835_isp_node_try_fmt, -+ .vidioc_try_fmt_meta_cap = bcm2835_isp_node_try_fmt, -+ .vidioc_s_selection = bcm2835_isp_node_s_selection, -+ .vidioc_g_selection = bcm2835_isp_node_g_selection, -+ -+ .vidioc_enum_fmt_vid_cap = bcm2835_isp_node_enum_fmt, -+ .vidioc_enum_fmt_vid_out = bcm2835_isp_node_enum_fmt, -+ .vidioc_enum_fmt_meta_cap = bcm2835_isp_node_enum_fmt, -+ -+ .vidioc_reqbufs = vb2_ioctl_reqbufs, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_expbuf = vb2_ioctl_expbuf, -+ .vidioc_create_bufs = vb2_ioctl_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ -+ .vidioc_subscribe_event = bcm3285_isp_subscribe_event, -+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -+}; -+ -+/* -+ * Size of the array to provide to the VPU when asking for the list of supported -+ * formats. -+ * -+ * The ISP component currently advertises 33 input formats, so add a small -+ * overhead on that. -+ */ -+#define MAX_SUPPORTED_ENCODINGS 40 -+ -+/* Populate node->supported_fmts with the formats supported by those ports. */ -+static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node) -+{ -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ struct bcm2835_isp_fmt *list; -+ unsigned int i, j, num_encodings; -+ u32 fourccs[MAX_SUPPORTED_ENCODINGS]; -+ u32 param_size = sizeof(fourccs); -+ int ret; -+ -+ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, -+ get_port_data(node), -+ MMAL_PARAMETER_SUPPORTED_ENCODINGS, -+ &fourccs, ¶m_size); -+ -+ if (ret) { -+ if (ret == MMAL_MSG_STATUS_ENOSPC) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: port has more encoding than we provided space for. Some are dropped.\n", -+ __func__); -+ num_encodings = MAX_SUPPORTED_ENCODINGS; -+ } else { -+ v4l2_err(&dev->v4l2_dev, "%s: get_param ret %u.\n", -+ __func__, ret); -+ return -EINVAL; -+ } -+ } else { -+ num_encodings = param_size / sizeof(u32); -+ } -+ -+ /* -+ * Assume at this stage that all encodings will be supported in V4L2. -+ * Any that aren't supported will waste a very small amount of memory. -+ */ -+ list = devm_kzalloc(dev->dev, -+ sizeof(struct bcm2835_isp_fmt) * num_encodings, -+ GFP_KERNEL); -+ if (!list) -+ return -ENOMEM; -+ node->supported_fmts.list = list; -+ -+ for (i = 0, j = 0; i < num_encodings; i++) { -+ const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]); -+ -+ if (fmt) { -+ list[j] = *fmt; -+ j++; -+ } -+ } -+ node->supported_fmts.num_entries = j; -+ -+ param_size = sizeof(fourccs); -+ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, -+ get_port_data(node), -+ MMAL_PARAMETER_SUPPORTED_ENCODINGS, -+ &fourccs, ¶m_size); -+ -+ if (ret) { -+ if (ret == MMAL_MSG_STATUS_ENOSPC) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: port has more encoding than we provided space for. Some are dropped.\n", -+ __func__); -+ num_encodings = MAX_SUPPORTED_ENCODINGS; -+ } else { -+ return -EINVAL; -+ } -+ } else { -+ num_encodings = param_size / sizeof(u32); -+ } -+ /* Assume at this stage that all encodings will be supported in V4L2. */ -+ list = devm_kzalloc(dev->dev, -+ sizeof(struct bcm2835_isp_fmt) * num_encodings, -+ GFP_KERNEL); -+ if (!list) -+ return -ENOMEM; -+ node->supported_fmts.list = list; -+ -+ for (i = 0, j = 0; i < num_encodings; i++) { -+ const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]); -+ -+ if (fmt) { -+ list[j] = *fmt; -+ j++; -+ } -+ } -+ node->supported_fmts.num_entries = j; -+ return 0; -+} -+ -+/* -+ * Register a device node /dev/video<N> to go along with one of the ISP's input -+ * or output nodes. -+ */ -+static int register_node(struct bcm2835_isp_dev *dev, -+ struct bcm2835_isp_node *node, -+ int index) -+{ -+ struct video_device *vfd; -+ struct vb2_queue *queue; -+ int ret; -+ -+ mutex_init(&node->lock); -+ -+ node->dev = dev; -+ vfd = &node->vfd; -+ queue = &node->queue; -+ queue->type = index_to_queue_type(index); -+ /* -+ * Setup the node type-specific params. -+ * -+ * Only the OUTPUT node can set controls and crop windows. However, -+ * we must allow the s/g_selection ioctl on the stats node as v4l2 -+ * compliance expects it to return a -ENOTTY, and the framework -+ * does not handle it if the ioctl is disabled. -+ */ -+ switch (queue->type) { -+ case V4L2_BUF_TYPE_VIDEO_OUTPUT: -+ vfd->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; -+ node->id = index; -+ node->vfl_dir = VFL_DIR_TX; -+ node->name = "output"; -+ break; -+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: -+ vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; -+ /* First Capture node starts at id 0, etc. */ -+ node->id = index - BCM2835_ISP_NUM_OUTPUTS; -+ node->vfl_dir = VFL_DIR_RX; -+ node->name = "capture"; -+ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL); -+ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_SELECTION); -+ v4l2_disable_ioctl(&node->vfd, VIDIOC_G_SELECTION); -+ break; -+ case V4L2_BUF_TYPE_META_CAPTURE: -+ vfd->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; -+ node->id = index - BCM2835_ISP_NUM_OUTPUTS; -+ node->vfl_dir = VFL_DIR_RX; -+ node->name = "stats"; -+ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL); -+ break; -+ } -+ -+ /* We use the selection API instead of the old crop API. */ -+ v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); -+ v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); -+ v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); -+ -+ ret = bcm2835_isp_get_supported_fmts(node); -+ if (ret) -+ return ret; -+ -+ /* Initialise the the video node. */ -+ vfd->vfl_type = VFL_TYPE_GRABBER; -+ vfd->fops = &bcm2835_isp_fops, -+ vfd->ioctl_ops = &bcm2835_isp_node_ioctl_ops, -+ vfd->minor = -1, -+ vfd->release = video_device_release_empty, -+ vfd->queue = &node->queue; -+ vfd->lock = &node->lock; -+ vfd->v4l2_dev = &dev->v4l2_dev; -+ vfd->vfl_dir = node->vfl_dir; -+ -+ node->q_data.fmt = get_default_format(node); -+ node->q_data.width = DEFAULT_DIM; -+ node->q_data.height = DEFAULT_DIM; -+ node->q_data.bytesperline = -+ get_bytesperline(DEFAULT_DIM, node->q_data.fmt); -+ node->q_data.sizeimage = node_is_stats(node) ? -+ get_port_data(node)->recommended_buffer.size : -+ get_sizeimage(node->q_data.bytesperline, -+ node->q_data.width, -+ node->q_data.height, -+ node->q_data.fmt); -+ -+ queue->io_modes = VB2_MMAP | VB2_DMABUF; -+ queue->drv_priv = node; -+ queue->ops = &bcm2835_isp_node_queue_ops; -+ queue->mem_ops = &vb2_dma_contig_memops; -+ queue->buf_struct_size = sizeof(struct bcm2835_isp_buffer); -+ queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; -+ queue->dev = dev->dev; -+ queue->lock = &node->queue_lock; -+ -+ ret = vb2_queue_init(queue); -+ if (ret < 0) { -+ v4l2_info(&dev->v4l2_dev, "vb2_queue_init failed\n"); -+ return ret; -+ } -+ node->queue_init = true; -+ -+ /* Define the device names */ -+ snprintf(vfd->name, sizeof(node->vfd.name), "%s-%s%d", BCM2835_ISP_NAME, -+ node->name, node->id); -+ -+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr + index); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to register video %s[%d] device node\n", -+ node->name, node->id); -+ return ret; -+ } -+ -+ node->registered = true; -+ video_set_drvdata(vfd, node); -+ -+ /* Set some controls and defaults, but only on the VIDEO_OUTPUT node. */ -+ if (node_is_output(node)) { -+ unsigned int i; -+ -+ /* Use this ctrl template to assign all out ISP custom ctrls. */ -+ struct v4l2_ctrl_config ctrl_template = { -+ .ops = &bcm2835_isp_ctrl_ops, -+ .type = V4L2_CTRL_TYPE_U8, -+ .def = 0, -+ .min = 0x00, -+ .max = 0xff, -+ .step = 1, -+ }; -+ -+ v4l2_ctrl_handler_init(&dev->ctrl_handler, 4); -+ -+ dev->r_gain = 1000; -+ dev->b_gain = 1000; -+ -+ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, -+ V4L2_CID_RED_BALANCE, 1, 0xffff, 1, -+ dev->r_gain); -+ -+ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, -+ V4L2_CID_BLUE_BALANCE, 1, 0xffff, 1, -+ dev->b_gain); -+ -+ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, -+ V4L2_CID_DIGITAL_GAIN, 1, 0xffff, 1, 1000); -+ -+ for (i = 0; i < ARRAY_SIZE(custom_ctrls); i++) { -+ ctrl_template.name = custom_ctrls[i].name; -+ ctrl_template.id = custom_ctrls[i].id; -+ ctrl_template.dims[0] = custom_ctrls[i].size; -+ ctrl_template.flags = custom_ctrls[i].flags; -+ v4l2_ctrl_new_custom(&dev->ctrl_handler, -+ &ctrl_template, NULL); -+ } -+ -+ node->vfd.ctrl_handler = &dev->ctrl_handler; -+ } -+ -+ v4l2_info(&dev->v4l2_dev, -+ "Device node %s[%d] registered as /dev/video%d\n", -+ node->name, node->id, vfd->num); -+ -+ return 0; -+} -+ -+/* Unregister one of the /dev/video<N> nodes associated with the ISP. */ -+static void unregister_node(struct bcm2835_isp_node *node) -+{ -+ struct bcm2835_isp_dev *dev = node_get_dev(node); -+ -+ v4l2_info(&dev->v4l2_dev, -+ "Unregistering node %s[%d] device node /dev/video%d\n", -+ node->name, node->id, node->vfd.num); -+ -+ if (node->queue_init) -+ vb2_queue_release(&node->queue); -+ -+ if (node->registered) { -+ video_unregister_device(&node->vfd); -+ if (node_is_output(node)) -+ v4l2_ctrl_handler_free(&dev->ctrl_handler); -+ } -+ -+ /* -+ * node->supported_fmts.list is free'd automatically -+ * as a managed resource. -+ */ -+ node->supported_fmts.list = NULL; -+ node->supported_fmts.num_entries = 0; -+ node->vfd.ctrl_handler = NULL; -+ node->registered = false; -+ node->queue_init = false; -+} -+ -+static void media_controller_unregister(struct bcm2835_isp_dev *dev) -+{ -+ unsigned int i; -+ -+ v4l2_info(&dev->v4l2_dev, "Unregister from media controller\n"); -+ -+ if (dev->media_device_registered) { -+ media_device_unregister(&dev->mdev); -+ media_device_cleanup(&dev->mdev); -+ dev->media_device_registered = false; -+ } -+ -+ kfree(dev->entity.name); -+ dev->entity.name = NULL; -+ -+ if (dev->media_entity_registered) { -+ media_device_unregister_entity(&dev->entity); -+ dev->media_entity_registered = false; -+ } -+ -+ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { -+ struct bcm2835_isp_node *node = &dev->node[i]; -+ -+ if (node->media_node_registered) { -+ media_remove_intf_links(node->intf_link->intf); -+ media_entity_remove_links(&dev->node[i].vfd.entity); -+ media_devnode_remove(node->intf_devnode); -+ media_device_unregister_entity(&node->vfd.entity); -+ kfree(node->vfd.entity.name); -+ } -+ node->media_node_registered = false; -+ } -+ -+ dev->v4l2_dev.mdev = NULL; -+} -+ -+static int media_controller_register_node(struct bcm2835_isp_dev *dev, int num) -+{ -+ struct bcm2835_isp_node *node = &dev->node[num]; -+ struct media_entity *entity = &node->vfd.entity; -+ int output = node_is_output(node); -+ char *name; -+ int ret; -+ -+ v4l2_info(&dev->v4l2_dev, -+ "Register %s node %d with media controller\n", -+ output ? "output" : "capture", num); -+ entity->obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE; -+ entity->function = MEDIA_ENT_F_IO_V4L; -+ entity->info.dev.major = VIDEO_MAJOR; -+ entity->info.dev.minor = node->vfd.minor; -+ name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL); -+ if (!name) { -+ ret = -ENOMEM; -+ goto error_no_mem; -+ } -+ snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "%s0-%s%d", -+ BCM2835_ISP_NAME, output ? "output" : "capture", num); -+ entity->name = name; -+ node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; -+ ret = media_entity_pads_init(entity, 1, &node->pad); -+ if (ret) -+ goto error_pads_init; -+ ret = media_device_register_entity(&dev->mdev, entity); -+ if (ret) -+ goto error_register_entity; -+ -+ node->intf_devnode = media_devnode_create(&dev->mdev, -+ MEDIA_INTF_T_V4L_VIDEO, 0, -+ VIDEO_MAJOR, node->vfd.minor); -+ if (!node->intf_devnode) { -+ ret = -ENOMEM; -+ goto error_devnode_create; -+ } -+ -+ node->intf_link = media_create_intf_link(entity, -+ &node->intf_devnode->intf, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (!node->intf_link) { -+ ret = -ENOMEM; -+ goto error_create_intf_link; -+ } -+ -+ if (output) -+ ret = media_create_pad_link(entity, 0, &dev->entity, num, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ else -+ ret = media_create_pad_link(&dev->entity, num, entity, 0, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (ret) -+ goto error_create_pad_link; -+ -+ dev->node[num].media_node_registered = true; -+ return 0; -+ -+error_create_pad_link: -+ media_remove_intf_links(&node->intf_devnode->intf); -+error_create_intf_link: -+ media_devnode_remove(node->intf_devnode); -+error_devnode_create: -+ media_device_unregister_entity(&node->vfd.entity); -+error_register_entity: -+error_pads_init: -+ kfree(entity->name); -+ entity->name = NULL; -+error_no_mem: -+ if (ret) -+ v4l2_info(&dev->v4l2_dev, "Error registering node\n"); -+ -+ return ret; -+} -+ -+static int media_controller_register(struct bcm2835_isp_dev *dev) -+{ -+ char *name; -+ unsigned int i; -+ int ret; -+ -+ v4l2_dbg(2, debug, &dev->v4l2_dev, "Registering with media controller\n"); -+ dev->mdev.dev = dev->dev; -+ strscpy(dev->mdev.model, "bcm2835-isp", -+ sizeof(dev->mdev.model)); -+ strscpy(dev->mdev.bus_info, "platform:bcm2835-isp", -+ sizeof(dev->mdev.bus_info)); -+ media_device_init(&dev->mdev); -+ dev->v4l2_dev.mdev = &dev->mdev; -+ -+ v4l2_dbg(2, debug, &dev->v4l2_dev, "Register entity for nodes\n"); -+ -+ name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL); -+ if (!name) { -+ ret = -ENOMEM; -+ goto done; -+ } -+ snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "bcm2835_isp0"); -+ dev->entity.name = name; -+ dev->entity.obj_type = MEDIA_ENTITY_TYPE_BASE; -+ dev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; -+ -+ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { -+ dev->pad[i].flags = node_is_output(&dev->node[i]) ? -+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; -+ } -+ -+ ret = media_entity_pads_init(&dev->entity, BCM2835_ISP_NUM_NODES, -+ dev->pad); -+ if (ret) -+ goto done; -+ -+ ret = media_device_register_entity(&dev->mdev, &dev->entity); -+ if (ret) -+ goto done; -+ -+ dev->media_entity_registered = true; -+ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { -+ ret = media_controller_register_node(dev, i); -+ if (ret) -+ goto done; -+ } -+ -+ ret = media_device_register(&dev->mdev); -+ if (!ret) -+ dev->media_device_registered = true; -+done: -+ return ret; -+} -+ -+static int bcm2835_isp_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_isp_dev *dev = platform_get_drvdata(pdev); -+ unsigned int i; -+ -+ media_controller_unregister(dev); -+ -+ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) -+ unregister_node(&dev->node[i]); -+ -+ v4l2_device_unregister(&dev->v4l2_dev); -+ -+ if (dev->component) -+ vchiq_mmal_component_finalise(dev->mmal_instance, -+ dev->component); -+ -+ vchiq_mmal_finalise(dev->mmal_instance); -+ -+ return 0; -+} -+ -+static int bcm2835_isp_probe(struct platform_device *pdev) -+{ -+ struct bcm2835_isp_dev *dev; -+ unsigned int i; -+ int ret; -+ -+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); -+ if (!dev) -+ return -ENOMEM; -+ -+ dev->dev = &pdev->dev; -+ -+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); -+ if (ret) -+ return ret; -+ -+ ret = vchiq_mmal_init(&dev->mmal_instance); -+ if (ret) { -+ v4l2_device_unregister(&dev->v4l2_dev); -+ return ret; -+ } -+ -+ ret = vchiq_mmal_component_init(dev->mmal_instance, "ril.isp", -+ &dev->component); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: failed to create ril.isp component\n", __func__); -+ goto error; -+ } -+ -+ if ((dev->component->inputs != BCM2835_ISP_NUM_OUTPUTS) || -+ (dev->component->outputs != BCM2835_ISP_NUM_CAPTURES + -+ BCM2835_ISP_NUM_METADATA)) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: ril.isp returned %d i/p (%d expected), %d o/p (%d expected) ports\n", -+ __func__, dev->component->inputs, -+ BCM2835_ISP_NUM_OUTPUTS, -+ dev->component->outputs, -+ BCM2835_ISP_NUM_CAPTURES + BCM2835_ISP_NUM_METADATA); -+ goto error; -+ } -+ -+ atomic_set(&dev->num_streaming, 0); -+ -+ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { -+ struct bcm2835_isp_node *node = &dev->node[i]; -+ -+ ret = register_node(dev, node, i); -+ if (ret) -+ goto error; -+ } -+ -+ ret = media_controller_register(dev); -+ if (ret) -+ goto error; -+ -+ platform_set_drvdata(pdev, dev); -+ v4l2_info(&dev->v4l2_dev, "Loaded V4L2 %s\n", BCM2835_ISP_NAME); -+ return 0; -+ -+error: -+ bcm2835_isp_remove(pdev); -+ -+ return ret; -+} -+ -+static struct platform_driver bcm2835_isp_pdrv = { -+ .probe = bcm2835_isp_probe, -+ .remove = bcm2835_isp_remove, -+ .driver = { -+ .name = BCM2835_ISP_NAME, -+ }, -+}; -+ -+module_platform_driver(bcm2835_isp_pdrv); -+ -+MODULE_DESCRIPTION("BCM2835 ISP driver"); -+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>"); -+MODULE_LICENSE("GPL"); -+MODULE_VERSION("1.0"); -+MODULE_ALIAS("platform:bcm2835-isp"); ---- /dev/null -+++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h -@@ -0,0 +1,67 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Broadcom BCM2835 ISP driver -+ * -+ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. -+ * -+ * Author: Naushir Patuck (naush@raspberrypi.com) -+ * -+ */ -+ -+#ifndef BCM2835_ISP_CTRLS -+#define BCM2835_ISP_CTRLS -+ -+#include <linux/bcm2835-isp.h> -+ -+struct bcm2835_isp_custom_ctrl { -+ const char *name; -+ u32 id; -+ u32 size; -+ u32 flags; -+}; -+ -+static const struct bcm2835_isp_custom_ctrl custom_ctrls[] = { -+ { -+ .name = "Colour Correction Matrix", -+ .id = V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, -+ .size = sizeof(struct bcm2835_isp_custom_ccm), -+ .flags = 0 -+ }, { -+ .name = "Lens Shading", -+ .id = V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, -+ .size = sizeof(struct bcm2835_isp_lens_shading), -+ .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE -+ }, { -+ .name = "Black Level", -+ .id = V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, -+ .size = sizeof(struct bcm2835_isp_black_level), -+ .flags = 0 -+ }, { -+ .name = "Green Equalisation", -+ .id = V4L2_CID_USER_BCM2835_ISP_GEQ, -+ .size = sizeof(struct bcm2835_isp_geq), -+ .flags = 0 -+ }, { -+ .name = "Gamma", -+ .id = V4L2_CID_USER_BCM2835_ISP_GAMMA, -+ .size = sizeof(struct bcm2835_isp_gamma), -+ .flags = 0 -+ }, { -+ .name = "Sharpen", -+ .id = V4L2_CID_USER_BCM2835_ISP_SHARPEN, -+ .size = sizeof(struct bcm2835_isp_sharpen), -+ .flags = 0 -+ }, { -+ .name = "Denoise", -+ .id = V4L2_CID_USER_BCM2835_ISP_DENOISE, -+ .size = sizeof(struct bcm2835_isp_denoise), -+ .flags = 0 -+ }, { -+ .name = "Defective Pixel Correction", -+ .id = V4L2_CID_USER_BCM2835_ISP_DPC, -+ .size = sizeof(struct bcm2835_isp_dpc), -+ .flags = 0 -+ } -+}; -+ -+#endif ---- /dev/null -+++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h -@@ -0,0 +1,272 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Broadcom BCM2835 ISP driver -+ * -+ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. -+ * -+ * Author: Naushir Patuck (naush@raspberrypi.com) -+ * -+ */ -+ -+#ifndef BCM2835_ISP_FMTS -+#define BCM2835_ISP_FMTS -+ -+#include <linux/videodev2.h> -+#include "vchiq-mmal/mmal-encodings.h" -+ -+struct bcm2835_isp_fmt { -+ u32 fourcc; -+ int depth; -+ int bytesperline_align; -+ u32 flags; -+ u32 mmal_fmt; -+ int size_multiplier_x2; -+ enum v4l2_colorspace colorspace; -+}; -+ -+struct bcm2835_isp_fmt_list { -+ struct bcm2835_isp_fmt *list; -+ unsigned int num_entries; -+}; -+ -+static const struct bcm2835_isp_fmt supported_formats[] = { -+ { -+ /* YUV formats */ -+ .fourcc = V4L2_PIX_FMT_YUV420, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_I420, -+ .size_multiplier_x2 = 3, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YVU420, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_YV12, -+ .size_multiplier_x2 = 3, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV12, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_NV12, -+ .size_multiplier_x2 = 3, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV21, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_NV21, -+ .size_multiplier_x2 = 3, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUYV, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_YUYV, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ .fourcc = V4L2_PIX_FMT_UYVY, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_UYVY, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YVYU, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_YVYU, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ .fourcc = V4L2_PIX_FMT_VYUY, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_VYUY, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SMPTE170M, -+ }, { -+ /* RGB formats */ -+ .fourcc = V4L2_PIX_FMT_RGB24, -+ .depth = 24, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_RGB24, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SRGB, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGB565, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_RGB16, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SRGB, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .depth = 24, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BGR24, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SRGB, -+ }, { -+ .fourcc = V4L2_PIX_FMT_ABGR32, -+ .depth = 32, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BGRA, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_SRGB, -+ }, { -+ /* Bayer formats */ -+ /* 8 bit */ -+ .fourcc = V4L2_PIX_FMT_SRGGB8, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB8, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SBGGR8, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR8, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGRBG8, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG8, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGBRG8, -+ .depth = 8, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG8, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ /* 10 bit */ -+ .fourcc = V4L2_PIX_FMT_SRGGB10P, -+ .depth = 10, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SBGGR10P, -+ .depth = 10, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGRBG10P, -+ .depth = 10, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGBRG10P, -+ .depth = 10, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ /* 12 bit */ -+ .fourcc = V4L2_PIX_FMT_SRGGB12P, -+ .depth = 12, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SBGGR12P, -+ .depth = 12, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGRBG12P, -+ .depth = 12, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGBRG12P, -+ .depth = 12, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12P, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ /* 16 bit */ -+ .fourcc = V4L2_PIX_FMT_SRGGB16, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB16, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SBGGR16, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR16, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGRBG16, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG16, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ .fourcc = V4L2_PIX_FMT_SGBRG16, -+ .depth = 16, -+ .bytesperline_align = 32, -+ .flags = 0, -+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG16, -+ .size_multiplier_x2 = 2, -+ .colorspace = V4L2_COLORSPACE_RAW, -+ }, { -+ /* ISP statistics format */ -+ .fourcc = V4L2_META_FMT_BCM2835_ISP_STATS, -+ .mmal_fmt = MMAL_ENCODING_BRCM_STATS, -+ /* The rest are not valid fields for stats. */ -+ } -+}; -+ -+#endif ---- a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h -+++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h -@@ -100,6 +100,10 @@ - */ - #define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') - -+/** ISP image statistics format -+ */ -+#define MMAL_ENCODING_BRCM_STATS MMAL_FOURCC('S', 'T', 'A', 'T') -+ - /* }@ */ - - /** \name Pre-defined audio encodings */ ---- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h -+++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h -@@ -221,6 +221,62 @@ enum mmal_parameter_camera_type { - MMAL_PARAMETER_SHUTTER_SPEED, - /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */ - MMAL_PARAMETER_CUSTOM_AWB_GAINS, -+ /**< Takes a @ref MMAL_PARAMETER_CAMERA_SETTINGS_T */ -+ MMAL_PARAMETER_CAMERA_SETTINGS, -+ /**< Takes a @ref MMAL_PARAMETER_PRIVACY_INDICATOR_T */ -+ MMAL_PARAMETER_PRIVACY_INDICATOR, -+ /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_DENOISE, -+ /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_STILLS_DENOISE, -+ /**< Takes a @ref MMAL_PARAMETER_CAMERA_ANNOTATE_T */ -+ MMAL_PARAMETER_ANNOTATE, -+ /**< Takes a @ref MMAL_PARAMETER_STEREOSCOPIC_MODE_T */ -+ MMAL_PARAMETER_STEREOSCOPIC_MODE, -+ /**< Takes a @ref MMAL_PARAMETER_CAMERA_INTERFACE_T */ -+ MMAL_PARAMETER_CAMERA_INTERFACE, -+ /**< Takes a @ref MMAL_PARAMETER_CAMERA_CLOCKING_MODE_T */ -+ MMAL_PARAMETER_CAMERA_CLOCKING_MODE, -+ /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_CONFIG_T */ -+ MMAL_PARAMETER_CAMERA_RX_CONFIG, -+ /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_TIMING_T */ -+ MMAL_PARAMETER_CAMERA_RX_TIMING, -+ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_DPF_CONFIG, -+ -+ /* 0x50 */ -+ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_JPEG_RESTART_INTERVAL, -+ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CAMERA_ISP_BLOCK_OVERRIDE, -+ /**< Takes a @ref MMAL_PARAMETER_LENS_SHADING_T */ -+ MMAL_PARAMETER_LENS_SHADING_OVERRIDE, -+ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_BLACK_LEVEL, -+ /**< Takes a @ref MMAL_PARAMETER_RESIZE_T */ -+ MMAL_PARAMETER_RESIZE_PARAMS, -+ /**< Takes a @ref MMAL_PARAMETER_CROP_T */ -+ MMAL_PARAMETER_CROP, -+ /**< Takes a @ref MMAL_PARAMETER_INT32_T */ -+ MMAL_PARAMETER_OUTPUT_SHIFT, -+ /**< Takes a @ref MMAL_PARAMETER_INT32_T */ -+ MMAL_PARAMETER_CCM_SHIFT, -+ /**< Takes a @ref MMAL_PARAMETER_CUSTOM_CCM_T */ -+ MMAL_PARAMETER_CUSTOM_CCM, -+ /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_ANALOG_GAIN, -+ /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_DIGITAL_GAIN, -+ /**< Takes a @ref MMAL_PARAMETER_DENOISE_T */ -+ MMAL_PARAMETER_DENOISE, -+ /**< Takes a @ref MMAL_PARAMETER_SHARPEN_T */ -+ MMAL_PARAMETER_SHARPEN, -+ /**< Takes a @ref MMAL_PARAMETER_GEQ_T */ -+ MMAL_PARAMETER_GEQ, -+ /**< Tales a @ref MMAP_PARAMETER_DPC_T */ -+ MMAL_PARAMETER_DPC, -+ /**< Tales a @ref MMAP_PARAMETER_GAMMA_T */ -+ MMAL_PARAMETER_GAMMA, - }; - - struct mmal_parameter_rational { -@@ -780,7 +836,102 @@ struct mmal_parameter_camera_info { - struct mmal_parameter_camera_info_camera - cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS]; - struct mmal_parameter_camera_info_flash -- flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; -+ flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; -+}; -+ -+struct mmal_parameter_ccm { -+ struct mmal_parameter_rational ccm[3][3]; -+ s32 offsets[3]; -+}; -+ -+struct mmal_parameter_custom_ccm { -+ u32 enabled; /**< Enable the custom CCM. */ -+ struct mmal_parameter_ccm ccm; /**< CCM to be used. */ -+}; -+ -+struct mmal_parameter_lens_shading { -+ u32 enabled; -+ u32 grid_cell_size; -+ u32 grid_width; -+ u32 grid_stride; -+ u32 grid_height; -+ u32 mem_handle_table; -+ u32 ref_transform; -+}; -+ -+enum mmal_parameter_ls_gain_format_type { -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U0P8_1 = 0, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_0 = 1, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_1 = 2, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_0 = 3, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_1 = 4, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_0 = 5, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_1 = 6, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U4P10 = 7, -+ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_DUMMY = 0x7FFFFFFF -+}; -+ -+struct mmal_parameter_lens_shading_v2 { -+ u32 enabled; -+ u32 grid_cell_size; -+ u32 grid_width; -+ u32 grid_stride; -+ u32 grid_height; -+ u32 mem_handle_table; -+ u32 ref_transform; -+ u32 corner_sampled; -+ enum mmal_parameter_ls_gain_format_type gain_format; -+}; -+ -+struct mmal_parameter_black_level { -+ u32 enabled; -+ u16 black_level_r; -+ u16 black_level_g; -+ u16 black_level_b; -+ u8 pad_[2]; /* Unused */ -+}; -+ -+struct mmal_parameter_geq { -+ u32 enabled; -+ u32 offset; -+ struct mmal_parameter_rational slope; -+}; -+ -+#define MMAL_NUM_GAMMA_PTS 33 -+struct mmal_parameter_gamma { -+ u32 enabled; -+ u16 x[MMAL_NUM_GAMMA_PTS]; -+ u16 y[MMAL_NUM_GAMMA_PTS]; -+}; -+ -+struct mmal_parameter_denoise { -+ u32 enabled; -+ u32 constant; -+ struct mmal_parameter_rational slope; -+ struct mmal_parameter_rational strength; -+}; -+ -+struct mmal_parameter_sharpen { -+ u32 enabled; -+ struct mmal_parameter_rational threshold; -+ struct mmal_parameter_rational strength; -+ struct mmal_parameter_rational limit; -+}; -+ -+enum mmal_dpc_mode { -+ MMAL_DPC_MODE_OFF = 0, -+ MMAL_DPC_MODE_NORMAL = 1, -+ MMAL_DPC_MODE_STRONG = 2, -+ MMAL_DPC_MODE_MAX = 0x7FFFFFFF, -+}; -+ -+struct mmal_parameter_dpc { -+ u32 enabled; -+ u32 strength; -+}; -+ -+struct mmal_parameter_crop { -+ struct vchiq_mmal_rect rect; - }; - - #endif |