Hi Hans, Thank you for the review and comments. Just a follow up and overlapping Dave's comments: On Mon, 18 May 2020 at 13:02, Hans Verkuil <hverkuil@xxxxxxxxx> wrote: > > On 04/05/2020 11:26, Laurent Pinchart wrote: > > From: Naushir Patuck <naush@xxxxxxxxxxxxxxx> > > > > Port the V4L2 compatible driver for the ISP unit found on Broadcom BCM2835 > > chips. > > > > The driver interfaces though the VideoCore unit using the VCHIQ MMAL > > interface. > > > > ISP driver upported from from RaspberryPi BSP at revision: > > 6c3505be6c3e ("staging: vc04_services: isp: Make all references to bcm2835_isp_fmt const") > > > > Signed-off-by: Dave Stevenson <dave.stevenson@xxxxxxxxxxxxxxx> > > Signed-off-by: Naushir Patuck <naush@xxxxxxxxxxxxxxx> > > [Adapt to staging by moving all modifications that in the BSP are scattered > > in core components inside this directory] > > Signed-off-by: Jacopo Mondi <jacopo@xxxxxxxxxx> > > --- > > .../v4l/pixfmt-meta-bcm2835-isp-stats.rst | 41 + > > drivers/staging/vc04_services/Kconfig | 2 + > > drivers/staging/vc04_services/Makefile | 1 + > > .../staging/vc04_services/bcm2835-isp/Kconfig | 14 + > > .../vc04_services/bcm2835-isp/Makefile | 10 + > > .../bcm2835-isp/bcm2835-v4l2-isp.c | 1632 +++++++++++++++++ > > .../bcm2835-isp/bcm2835_isp_ctrls.h | 67 + > > .../bcm2835-isp/bcm2835_isp_fmts.h | 301 +++ > > .../include/uapi/linux/bcm2835-isp.h | 333 ++++ > > .../staging/vc04_services/vchiq-mmal/Kconfig | 3 +- > > .../vc04_services/vchiq-mmal/mmal-encodings.h | 4 + > > .../vchiq-mmal/mmal-parameters.h | 153 +- > > 12 files changed, 2559 insertions(+), 2 deletions(-) > > create mode 100644 drivers/staging/vc04_services/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst > > 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 > > create mode 100644 drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h > > > > <snip> > > > diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c > > new file mode 100644 > > index 000000000000..a32faab4b8dc > > --- /dev/null > > +++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c > > @@ -0,0 +1,1632 @@ > > <snip> > > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Broadcom BCM2835 ISP driver > > + * > > + * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. > > + * > > + * Author: Naushir Patuck (naush@xxxxxxxxxxxxxxx) > > + * > > + */ > > + > > +#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; > > + const 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 const > > +struct bcm2835_isp_fmt *find_format_by_fourcc(unsigned int fourcc, > > + struct bcm2835_isp_node *node) > > +{ > > + struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; > > + const struct bcm2835_isp_fmt *fmt; > > + unsigned int i; > > + > > + for (i = 0; i < fmts->num_entries; i++) { > > + fmt = fmts->list[i]; > > + if (fmt->fourcc == fourcc) > > + return fmt; > > + } > > + > > + return NULL; > > +} > > + > > +static const > > +struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, > > + struct bcm2835_isp_node *node) > > +{ > > + return find_format_by_fourcc(node_is_stats(node) ? > > + f->fmt.meta.dataformat : > > + f->fmt.pix.pixelformat, > > + node); > > +} > > + > > +/* 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); > > + > > It's not obvious from this code what happens with outstanding buffers if there > is an error. They should be returned to vb2 with state VB2_BUF_STATE_QUEUED. > Does that happen? > > > + 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 const > > +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, > > + const 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, > > + const 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_enum_framesizes(struct file *file, void *priv, > > + struct v4l2_frmsizeenum *fsize) > > +{ > > + struct bcm2835_isp_node *node = video_drvdata(file); > > + struct bcm2835_isp_dev *dev = node_get_dev(node); > > + const struct bcm2835_isp_fmt *fmt; > > + > > + if (node_is_stats(node) || fsize->index) > > + return -EINVAL; > > + > > + fmt = find_format_by_fourcc(fsize->pixel_format, node); > > + if (!fmt) { > > + v4l2_err(&dev->v4l2_dev, "Invalid pixel code: %x\n", > > + fsize->pixel_format); > > + return -EINVAL; > > + } > > + > > + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; > > + fsize->stepwise.min_width = MIN_DIM; > > + fsize->stepwise.max_width = MAX_DIM; > > + fsize->stepwise.step_width = fmt->step_size; > > + > > + fsize->stepwise.min_height = MIN_DIM; > > + fsize->stepwise.max_height = MAX_DIM; > > + fsize->stepwise.step_height = fmt->step_size; > > + > > + return 0; > > +} > > + > > +static int bcm2835_isp_node_try_fmt(struct file *file, void *priv, > > + struct v4l2_format *f) > > +{ > > + struct bcm2835_isp_node *node = video_drvdata(file); > > + const 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; > > I'm missing a check for s->target. Ack > > > + > > + /* 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; > > No support for _TGT_CROP_DEFAULT/BOUNDS? Those are usually supported > by drivers and are typically set to the width/height of the current > format. > > I recommend adding support for these targets. Ack > > > + > > + 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_enum_framesizes = bcm2835_isp_enum_framesizes, > > + > > + .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 const **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; > > + > > + 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); > > Why not disable S/G_SELECTION for meta capture here rather than test for it > in the op functions? The impelmenation with S/G_SELECTION for meta capture disabled caused problem with v4l2-compliance. However, with the other changes proposed in this review, it does seem to now work correcly. So I will update this code and disable the ioctl for the meta capture node. > > > + 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); > > No need for this: the core handles this and will disable these ioctls > automatically. Without these explicitally disabled, we fail v4l2-compliance on the video capture nodes with the following error: Format ioctls: test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK test VIDIOC_G/S_PARM: OK (Not Supported) test VIDIOC_G_FBUF: OK (Not Supported) test VIDIOC_G_FMT: OK test VIDIOC_TRY_FMT: OK test VIDIOC_S_FMT: OK test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported) fail: v4l2-test-formats.cpp(1510): doioctl(node, VIDIOC_G_SELECTION, &sel) fail: v4l2-test-formats.cpp(1550): testLegacyCrop(node) test Cropping: FAIL test Composing: OK (Not Supported) test Scaling: OK (Not Supported) > > > + > > + ret = bcm2835_isp_get_supported_fmts(node); > > + if (ret) > > + return ret; > > + > > + /* Initialise the the video node. */ > > + vfd->vfl_type = VFL_TYPE_VIDEO; > > + 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_VIDEO, 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; > > + } > > Move registering the video device to the end of this function. > Otherwise the output video device would be created (and available for > userspace) before the controls are added. > Ack > > + > > + 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; > > Missing error check. Ack > > > + } > > + > > + 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@xxxxxxxxxxxxxxx>"); > > +MODULE_LICENSE("GPL"); > > +MODULE_VERSION("1.0"); > > +MODULE_ALIAS("platform:bcm2835-isp"); > > diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h > > new file mode 100644 > > index 000000000000..cfbb1063aad1 > > --- /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@xxxxxxxxxxxxxxx) > > + * > > + */ > > + > > +#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 > > diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h > > new file mode 100644 > > index 000000000000..af3bde152bb2 > > --- /dev/null > > +++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h > > @@ -0,0 +1,301 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Broadcom BCM2835 ISP driver > > + * > > + * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. > > + * > > + * Author: Naushir Patuck (naush@xxxxxxxxxxxxxxx) > > + * > > + */ > > + > > +#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; > > + unsigned int step_size; > > +}; > > + > > +struct bcm2835_isp_fmt_list { > > + struct bcm2835_isp_fmt const **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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + /* 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, > > + .step_size = 1, > > + }, { > > + .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, > > + .step_size = 1, > > + }, { > > + .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, > > + .step_size = 1, > > + }, { > > + .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, > > + .step_size = 1, > > + }, { > > + /* 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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + /* 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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + /* 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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + /* 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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + .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, > > + .step_size = 2, > > + }, { > > + /* 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 > > diff --git a/drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h b/drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h > > new file mode 100644 > > index 000000000000..edc452fa8318 > > --- /dev/null > > +++ b/drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h > > @@ -0,0 +1,333 @@ > > +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ > > +/* > > + * bcm2835-isp.h > > + * > > + * BCM2835 ISP driver - user space header file. > > + * > > + * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. > > + * > > + * Author: Naushir Patuck (naush@xxxxxxxxxxxxxxx) > > + * > > + */ > > + > > +#ifndef __BCM2835_ISP_H_ > > +#define __BCM2835_ISP_H_ > > + > > +#include <linux/v4l2-controls.h> > > + > > +/* TODO: move the control IDs definitions to v4l2-controls.h */ > > +#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10c0) > > As the TODO says: move this to v4l2-controls.h. Currently the 0x10c0 offset > clashes with V4L2_CID_USER_ATMEL_ISC_BASE, so that certainly should be fixed. > Unfortunately, there seems to be a mixup here. Laurent, we have accidentally mailed a WIP revision of this patch. The final version does have V4L2_CID_USER_BCM2835_ISP_BASE with a unique id in v4l2-controls.h. I will talk with Laurent separately to get the correct revison included in the next patch-set. > > + > > +/* TODO: move the formats definitions to videodev2.h */ > > +/* 12 Y/CbCr 4:2:0 128 pixel wide column */ > > +#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') > > +/* Y/CbCr 4:2:0 10bpc, 3x10 packed as 4 bytes in a 128 bytes / 96 pixel wide column */ > > +#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0') > > +/* Sensor Ancillary metadata */ > > +#define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') > > +/* BCM2835 ISP image statistics output */ > > +#define V4L2_META_FMT_BCM2835_ISP_STATS v4l2_fourcc('B', 'S', 'T', 'A') > > + Similarly, these have also been moved to the right header files. > > +#define V4L2_CID_USER_BCM2835_ISP_CC_MATRIX \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0001) > > +#define V4L2_CID_USER_BCM2835_ISP_LENS_SHADING \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0002) > > +#define V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0003) > > +#define V4L2_CID_USER_BCM2835_ISP_GEQ \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0004) > > +#define V4L2_CID_USER_BCM2835_ISP_GAMMA \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0005) > > +#define V4L2_CID_USER_BCM2835_ISP_DENOISE \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0006) > > +#define V4L2_CID_USER_BCM2835_ISP_SHARPEN \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0007) > > +#define V4L2_CID_USER_BCM2835_ISP_DPC \ > > + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0008) > > There is no documentation for these controls. Specifically, it doesn't > tell you which struct should be used. As above, the documentaiton is available in the newer patch. > > > + > > +/* > > + * All structs below are directly mapped onto the equivalent structs in > > + * drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h > > + * for convenience. > > + */ > > + > > +/** > > + * struct bcm2835_isp_rational - Rational value type. > > + * > > + * @num: Numerator. > > + * @den: Denominator. > > + */ > > +struct bcm2835_isp_rational { > > + __s32 num; > > + __s32 den; > > Wouldn't it make more sense if den is a __u32? Ack > > > +}; > > + > > +/** > > + * struct bcm2835_isp_ccm - Colour correction matrix. > > + * > > + * @ccm: 3x3 correction matrix coefficients. > > + * @offsets: 1x3 correction offsets. > > + */ > > +struct bcm2835_isp_ccm { > > + struct bcm2835_isp_rational ccm[3][3]; > > + __s32 offsets[3]; > > +}; > > + > > +/** > > + * struct bcm2835_isp_custom_ccm - Custom CCM applied with the > > + * V4L2_CID_USER_BCM2835_ISP_CC_MATRIX ctrl. > > + * > > + * @enabled: Enable custom CCM. > > + * @ccm: Custom CCM coefficients and offsets. > > + */ > > +struct bcm2835_isp_custom_ccm { > > + __u32 enabled; > > + struct bcm2835_isp_ccm ccm; > > +}; > > + > > +/** > > + * enum bcm2835_isp_gain_format - format of the gains in the lens shading > > + * tables used with the > > + * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING ctrl. > > + * > > + * @GAIN_FORMAT_U0P8_1: Gains are u0.8 format, starting at 1.0 > > + * @GAIN_FORMAT_U1P7_0: Gains are u1.7 format, starting at 0.0 > > + * @GAIN_FORMAT_U1P7_1: Gains are u1.7 format, starting at 1.0 > > + * @GAIN_FORMAT_U2P6_0: Gains are u2.6 format, starting at 0.0 > > + * @GAIN_FORMAT_U2P6_1: Gains are u2.6 format, starting at 1.0 > > + * @GAIN_FORMAT_U3P5_0: Gains are u3.5 format, starting at 0.0 > > + * @GAIN_FORMAT_U3P5_1: Gains are u3.5 format, starting at 1.0 > > + * @GAIN_FORMAT_U4P10: Gains are u4.10 format, starting at 0.0 > > + */ > > +enum bcm2835_isp_gain_format { > > + GAIN_FORMAT_U0P8_1 = 0, > > + GAIN_FORMAT_U1P7_0 = 1, > > + GAIN_FORMAT_U1P7_1 = 2, > > + GAIN_FORMAT_U2P6_0 = 3, > > + GAIN_FORMAT_U2P6_1 = 4, > > + GAIN_FORMAT_U3P5_0 = 5, > > + GAIN_FORMAT_U3P5_1 = 6, > > + GAIN_FORMAT_U4P10 = 7, > > +}; > > + > > +/** > > + * struct bcm2835_isp_lens_shading - Lens shading tables supplied with the > > + * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING > > + * ctrl. > > + * > > + * @enabled: Enable lens shading. > > + * @grid_cell_size: Size of grid cells in samples (16, 32, 64, 128 or 256). > > + * @grid_width: Width of lens shading tables in grid cells. > > + * @grid_stride: Row to row distance (in grid cells) between grid cells > > + * in the same horizontal location. > > + * @grid_height: Height of lens shading tables in grid cells. > > + * @mem_handle_table: Memory handle to the tables. > > What sort of handle is this? I.e. where does it come from? I believe there was a separte discusion about this. This is a vcsm handle that is used to store the table coefficients in memory that is accessible from Videocore. There is work in progress to update this to use dmabuf handles. > > > + * @ref_transform: Reference transform - unsupported, please pass zero. > > + * @corner_sampled: Whether the gains are sampled at the corner points > > + * of the grid cells or in the cell centres. > > + * @gain_format: Format of the gains (see enum &bcm2835_isp_gain_format). > > + */ > > +struct bcm2835_isp_lens_shading { > > + __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; > > + __u32 gain_format; > > +}; > > + > > +/** > > + * struct bcm2835_isp_black_level - Sensor black level set with the > > + * V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL ctrl. > > + * > > + * @enabled: Enable black level. > > + * @black_level_r: Black level for red channel. > > + * @black_level_g: Black level for green channels. > > + * @black_level_b: Black level for blue channel. > > + */ > > +struct bcm2835_isp_black_level { > > + __u32 enabled; > > + __u16 black_level_r; > > + __u16 black_level_g; > > + __u16 black_level_b; > > + __u8 pad_[2]; /* Unused */ > > I prefer 'padding' over 'pad_'. Ack > > > +}; > > + > > +/** > > + * struct bcm2835_isp_geq - Green equalisation parameters set with the > > + * V4L2_CID_USER_BCM2835_ISP_GEQ ctrl. > > + * > > + * @enabled: Enable green equalisation. > > + * @offset: Fixed offset of the green equalisation threshold. > > + * @slope: Slope of the green equalisation threshold. > > + */ > > +struct bcm2835_isp_geq { > > + __u32 enabled; > > + __u32 offset; > > + struct bcm2835_isp_rational slope; > > +}; > > + > > +#define BCM2835_NUM_GAMMA_PTS 33 > > + > > +/** > > + * struct bcm2835_isp_gamma - Gamma parameters set with the > > + * V4L2_CID_USER_BCM2835_ISP_GAMMA ctrl. > > + * > > + * @enabled: Enable gamma adjustment. > > + * @X: X values of the points defining the gamma curve. > > + * Values should be scaled to 16 bits. > > + * @Y: Y values of the points defining the gamma curve. > > + * Values should be scaled to 16 bits. > > I assume 0 == black and 0xffff == white (or max luminance)? > > And so typically x[0] == y[0] == 0 and x[32] == y[32] == 0xffff? > Typically yes, but it is not strictily true. We could have parameters that clip the signal above min (0) and below max (0xffff). > > + */ > > +struct bcm2835_isp_gamma { > > + __u32 enabled; > > + __u16 x[BCM2835_NUM_GAMMA_PTS]; > > + __u16 y[BCM2835_NUM_GAMMA_PTS]; > > +}; > > + > > +/** > > + * struct bcm2835_isp_denoise - Denoise parameters set with the > > + * V4L2_CID_USER_BCM2835_ISP_DENOISE ctrl. > > + * > > + * @enabled: Enable denoise. > > + * @constant: Fixed offset of the noise threshold. > > + * @slope: Slope of the noise threshold. > > + * @strength: Denoise strength between 0.0 (off) and 1.0 (maximum). > > + */ > > +struct bcm2835_isp_denoise { > > + __u32 enabled; > > + __u32 constant; > > + struct bcm2835_isp_rational slope; > > + struct bcm2835_isp_rational strength; > > +}; > > + > > +/** > > + * struct bcm2835_isp_sharpen - Sharpen parameters set with the > > + * V4L2_CID_USER_BCM2835_ISP_SHARPEN ctrl. > > + * > > + * @enabled: Enable sharpening. > > + * @threshold: Threshold at which to start sharpening pixels. > > + * @strength: Strength with which pixel sharpening increases. > > + * @limit: Limit to the amount of sharpening applied. > > + */ > > +struct bcm2835_isp_sharpen { > > + __u32 enabled; > > + struct bcm2835_isp_rational threshold; > > + struct bcm2835_isp_rational strength; > > + struct bcm2835_isp_rational limit; > > +}; > > + > > +/** > > + * enum bcm2835_isp_dpc_mode - defective pixel correction (DPC) strength. > > + * > > + * @DPC_MODE_OFF: No DPC. > > + * @DPC_MODE_NORMAL: Normal DPC. > > + * @DPC_MODE_STRONG: Strong DPC. > > + */ > > +enum bcm2835_isp_dpc_mode { > > + DPC_MODE_OFF = 0, > > + DPC_MODE_NORMAL = 1, > > + DPC_MODE_STRONG = 2, > > +}; > > + > > +/** > > + * struct bcm2835_isp_dpc - Defective pixel correction (DPC) parameters set > > + * with the V4L2_CID_USER_BCM2835_ISP_DPC ctrl. > > + * > > + * @enabled: Enable DPC. > > + * @strength: DPC strength (see enum &bcm2835_isp_dpc_mode). > > Isn't DPC_MODE_OFF equal to just setting 'enabled' to false? If so, > wouldn't the 'strength' field be sufficient? This is a bit of a quirk of the hardware pipeline. DPC_MODE_OFF still keeps the block enabled, but running (mostly) as a passthrough. The enabled field physically switches off the block. > > > + */ > > +struct bcm2835_isp_dpc { > > + __u32 enabled; > > + __u32 strength; > > +}; > > + > > +/* > > + * ISP statistics structures. > > + * > > + * The bcm2835_isp_stats structure is generated at the output of the > > + * statistics node. Note that this does not directly map onto the statistics > > + * output of the ISP HW. Instead, the MMAL firmware code maps the HW statistics > > + * to the bcm2835_isp_stats structure. > > + */ > > +#define DEFAULT_AWB_REGIONS_X 16 > > +#define DEFAULT_AWB_REGIONS_Y 12 > > + > > +#define NUM_HISTOGRAMS 2 > > +#define NUM_HISTOGRAM_BINS 128 > > +#define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y) > > +#define FLOATING_REGIONS 16 > > +#define AGC_REGIONS 16 > > +#define FOCUS_REGIONS 12 > > + > > +/** > > + * struct bcm2835_isp_stats_hist - Histogram statistics > > + * > > + * @r_hist: Red channel histogram. > > + * @g_hist: Combined green channel histogram. > > + * @b_hist: Blue channel histogram. > > + */ > > +struct bcm2835_isp_stats_hist { > > + __u32 r_hist[NUM_HISTOGRAM_BINS]; > > + __u32 g_hist[NUM_HISTOGRAM_BINS]; > > + __u32 b_hist[NUM_HISTOGRAM_BINS]; > > +}; > > + > > +/** > > + * struct bcm2835_isp_stats_region - Region sums. > > + * > > + * @counted: The number of 2x2 bayer tiles accumulated. > > + * @notcounted: The number of 2x2 bayer tiles not accumulated. > > + * @r_sum: Total sum of counted pixels in the red channel for a region. > > + * @g_sum: Total sum of counted pixels in the green channel for a region. > > + * @b_sum: Total sum of counted pixels in the blue channel for a region. > > + */ > > +struct bcm2835_isp_stats_region { > > + __u32 counted; > > + __u32 notcounted; > > + __u64 r_sum; > > + __u64 g_sum; > > + __u64 b_sum; > > +}; > > + > > +/** > > + * struct bcm2835_isp_stats_focus - Focus statistics. > > + * > > + * @contrast_val: Focus measure - accumulated output of the focus filter. > > + * In the first dimension, index [0] counts pixels below a > > + * preset threshold, and index [1] counts pixels above the > > + * threshold. In the second dimension, index [0] uses the > > + * first predefined filter, and index [1] uses the second > > + * predefined filter. > > + * @contrast_val_num: The number of counted pixels in the above accumulation. > > + */ > > +struct bcm2835_isp_stats_focus { > > + __u64 contrast_val[2][2]; > > + __u32 contrast_val_num[2][2]; > > +}; > > + > > +/** > > + * struct bcm2835_isp_stats - ISP statistics. > > + * > > + * @version: Version of the bcm2835_isp_stats structure. > > + * @size: Size of the bcm2835_isp_stats structure. > > + * @hist: Histogram statistics for the entire image. > > + * @awb_stats: Statistics for the regions defined for AWB calculations. > > + * @floating_stats: Statistics for arbitrarily placed (floating) regions. > > + * @agc_stats: Statistics for the regions defined for AGC calculations. > > + * @focus_stats: Focus filter statistics for the focus regions. > > + */ > > +struct bcm2835_isp_stats { > > + __u32 version; > > + __u32 size; > > + struct bcm2835_isp_stats_hist hist[NUM_HISTOGRAMS]; > > + struct bcm2835_isp_stats_region awb_stats[AWB_REGIONS]; > > + struct bcm2835_isp_stats_region floating_stats[FLOATING_REGIONS]; > > + struct bcm2835_isp_stats_region agc_stats[AGC_REGIONS]; > > + struct bcm2835_isp_stats_focus focus_stats[FOCUS_REGIONS]; > > +}; > > + > > +#endif /* __BCM2835_ISP_H_ */ > > diff --git a/drivers/staging/vc04_services/vchiq-mmal/Kconfig b/drivers/staging/vc04_services/vchiq-mmal/Kconfig > > index 106f71e709df..072f3c755a68 100644 > > --- a/drivers/staging/vc04_services/vchiq-mmal/Kconfig > > +++ b/drivers/staging/vc04_services/vchiq-mmal/Kconfig > > @@ -5,4 +5,5 @@ config BCM2835_VCHIQ_MMAL > > help > > Enables the MMAL API over VCHIQ interface as used for the > > majority of the multimedia services on VideoCore. > > - Defaults to Y when the Broadcomd BCM2835 camera host is selected. > > + Defaults to Y when the Broadcomd BCM2835 camera host or ISP are > > + selected. > > diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h > > index 44ba91aa6d47..8d904fcce388 100644 > > --- 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 */ > > diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h > > index 1793103b18fd..b3552af5cf8f 100644 > > --- 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 { > > @@ -779,7 +835,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 > > > > Regards, > > Hans Regards, Naush