diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch | 2255 |
1 files changed, 2255 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch b/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch new file mode 100644 index 0000000000..38015cc98a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch @@ -0,0 +1,2255 @@ +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 |