From f93851e802e3562d475ee0ae94f8c31e0fd1c13b Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 9 Oct 2020 10:40:27 +0100 Subject: [PATCH 01/11] staging: bcm2835-codec: Allow decode res changed before STREAMON(CAPTURE) The V4L2 stateful video decoder API requires that you can STREAMON on only the OUTPUT queue, feed in buffers, and wait for the SOURCE_CHANGE event. This requires that we enable the MMAL output port at the same time as the input port, because the output port is the one that creates the SOURCE_CHANGED event. Signed-off-by: Dave Stevenson --- .../bcm2835-codec/bcm2835-v4l2-codec.c | 137 +++++++++++++++--- 1 file changed, 117 insertions(+), 20 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index 8df0eb2b532a89..f8fe8effa06d59 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -1498,6 +1498,7 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, struct vb2_queue *vq; struct vchiq_mmal_port *port; bool update_capture_port = false; + bool reenable_port = false; int ret; v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "Setting format for type %d, wxh: %dx%d, fmt: %08x, size %u\n", @@ -1575,6 +1576,24 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, if (!port) return 0; + if (port->enabled) { + /* + * This should only ever happen with DECODE and the MMAL output + * port that has been enabled for resolution changed events. + * In this case no buffers have been allocated or sent to the + * component, so warn on that. + */ + WARN_ON(ctx->dev->role != DECODE || + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || + atomic_read(&port->buffers_with_vpu)); + + ret = vchiq_mmal_port_disable(ctx->dev->instance, port); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, "%s: Error disabling port update buffer count, ret %d\n", + __func__, ret); + reenable_port = true; + } + setup_mmal_port_format(ctx, q_data, port); ret = vchiq_mmal_port_set_format(ctx->dev->instance, port); if (ret) { @@ -1589,6 +1608,14 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, port->minimum_buffer.size); } + if (reenable_port) { + ret = vchiq_mmal_port_enable(ctx->dev->instance, + port, + op_buffer_cb); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling o/p port, ret %d\n", + __func__, ret); + } v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "Set format for type %d, wxh: %dx%d, fmt: %08x, size %u\n", f->type, q_data->crop_width, q_data->height, q_data->fmt->fourcc, q_data->sizeimage); @@ -2467,9 +2494,11 @@ static int bcm2835_codec_create_component(struct bcm2835_codec_ctx *ctx) setup_mmal_port_format(ctx, &ctx->q_data[V4L2_M2M_SRC], &ctx->component->input[0]); + ctx->component->input[0].cb_ctx = ctx; setup_mmal_port_format(ctx, &ctx->q_data[V4L2_M2M_DST], &ctx->component->output[0]); + ctx->component->output[0].cb_ctx = ctx; ret = vchiq_mmal_port_set_format(dev->instance, &ctx->component->input[0]); @@ -2724,6 +2753,24 @@ static void bcm2835_codec_buffer_cleanup(struct vb2_buffer *vb) bcm2835_codec_mmal_buf_cleanup(&buf->mmal); } +static void bcm2835_codec_flush_buffers(struct bcm2835_codec_ctx *ctx, + struct vchiq_mmal_port *port) +{ + int ret; + + while (atomic_read(&port->buffers_with_vpu)) { + v4l2_dbg(1, debug, &ctx->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(&ctx->frame_cmplt, + COMPLETE_TIMEOUT); + if (ret <= 0) { + v4l2_err(&ctx->dev->v4l2_dev, "%s: Timeout waiting for buffers to be returned - %d outstanding\n", + __func__, + atomic_read(&port->buffers_with_vpu)); + break; + } + } +} static int bcm2835_codec_start_streaming(struct vb2_queue *q, unsigned int count) { @@ -2731,7 +2778,7 @@ static int bcm2835_codec_start_streaming(struct vb2_queue *q, struct bcm2835_codec_dev *dev = ctx->dev; struct bcm2835_codec_q_data *q_data = get_q_data(ctx, q->type); struct vchiq_mmal_port *port = get_port_data(ctx, q->type); - int ret; + int ret = 0; v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: type: %d count %d\n", __func__, q->type, count); @@ -2746,6 +2793,34 @@ static int bcm2835_codec_start_streaming(struct vb2_queue *q, ctx->component_enabled = true; } + if (port->enabled) { + unsigned int num_buffers; + + init_completion(&ctx->frame_cmplt); + + /* + * This should only ever happen with DECODE and the MMAL output + * port that has been enabled for resolution changed events. + * In this case no buffers have been allocated or sent to the + * component, so warn on that. + */ + WARN_ON(ctx->dev->role != DECODE); + WARN_ON(q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + WARN_ON(atomic_read(&port->buffers_with_vpu)); + + /* + * Disable will reread the port format, so retain buffer count. + */ + num_buffers = port->current_buffer.num; + + ret = vchiq_mmal_port_disable(dev->instance, port); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, "%s: Error disabling port update buffer count, ret %d\n", + __func__, ret); + bcm2835_codec_flush_buffers(ctx, port); + port->current_buffer.num = num_buffers; + } + if (count < port->minimum_buffer.num) count = port->minimum_buffer.num; @@ -2760,6 +2835,22 @@ static int bcm2835_codec_start_streaming(struct vb2_queue *q, __func__, ret); } + if (dev->role == DECODE && + q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + !ctx->component->output[0].enabled) { + /* + * Decode needs to enable the MMAL output/V4L2 CAPTURE + * port at this point too so that we have everything + * set up for dynamic resolution changes. + */ + ret = vchiq_mmal_port_enable(dev->instance, + &ctx->component->output[0], + op_buffer_cb); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling o/p port, ret %d\n", + __func__, ret); + } + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { /* * Create the EOS buffer. @@ -2771,7 +2862,6 @@ static int bcm2835_codec_start_streaming(struct vb2_queue *q, &q_data->eos_buffer.mmal); q_data->eos_buffer_in_use = false; - port->cb_ctx = ctx; ret = vchiq_mmal_port_enable(dev->instance, port, ip_buffer_cb); @@ -2779,14 +2869,17 @@ static int bcm2835_codec_start_streaming(struct vb2_queue *q, v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling i/p port, ret %d\n", __func__, ret); } else { - port->cb_ctx = ctx; - ret = vchiq_mmal_port_enable(dev->instance, - port, - op_buffer_cb); - if (ret) - v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling o/p port, ret %d\n", - __func__, ret); + if (!port->enabled) { + ret = vchiq_mmal_port_enable(dev->instance, + port, + op_buffer_cb); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling o/p port, ret %d\n", + __func__, ret); + } } + v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Done, ret %d\n", + __func__, ret); return ret; } @@ -2825,17 +2918,21 @@ static void bcm2835_codec_stop_streaming(struct vb2_queue *q) __func__, V4L2_TYPE_IS_OUTPUT(q->type) ? "i/p" : "o/p", ret); - while (atomic_read(&port->buffers_with_vpu)) { - v4l2_dbg(1, debug, &ctx->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(&ctx->frame_cmplt, - COMPLETE_TIMEOUT); - if (ret <= 0) { - v4l2_err(&ctx->dev->v4l2_dev, "%s: Timeout waiting for buffers to be returned - %d outstanding\n", - __func__, - atomic_read(&port->buffers_with_vpu)); - break; - } + bcm2835_codec_flush_buffers(ctx, port); + + if (dev->role == DECODE && + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + ctx->component->input[0].enabled) { + /* + * For decode we need to keep the MMAL output port enabled for + * resolution changed events whenever the input is enabled. + */ + ret = vchiq_mmal_port_enable(dev->instance, + &ctx->component->output[0], + op_buffer_cb); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling o/p port, ret %d\n", + __func__, ret); } /* If both ports disabled, then disable the component */ From 12956f60d3c44e7c92b3cf9264b9b06ffdd1af9e Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Sep 2021 17:44:19 +0100 Subject: [PATCH 02/11] staging/bcm2835-codec: Do not send buffers to the VPU unless streaming With video decode we now enable both input and output ports on the component. This means that buffers will get passed to the VPU earlier than desired if they are queued befoer STREAMON. Check that the queue is streaming before sending buffers to the VPU. Signed-off-by: Dave Stevenson --- .../bcm2835-codec/bcm2835-v4l2-codec.c | 64 +++++++++++-------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index f8fe8effa06d59..4156018dbf7a1b 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -1244,35 +1244,47 @@ static void device_run(void *priv) v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: off we go\n", __func__); - src_buf = v4l2_m2m_buf_remove(&ctx->fh.m2m_ctx->out_q_ctx); - if (src_buf) { - m2m = container_of(src_buf, struct v4l2_m2m_buffer, vb); - src_m2m_buf = container_of(m2m, struct m2m_mmal_buffer, m2m); - vb2_to_mmal_buffer(src_m2m_buf, src_buf); - - ret = vchiq_mmal_submit_buffer(dev->instance, - &ctx->component->input[0], - &src_m2m_buf->mmal); - v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: Submitted ip buffer len %lu, pts %llu, flags %04x\n", - __func__, src_m2m_buf->mmal.length, - src_m2m_buf->mmal.pts, src_m2m_buf->mmal.mmal_flags); - if (ret) - v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed submitting ip buffer\n", - __func__); + if (ctx->fh.m2m_ctx->out_q_ctx.q.streaming) { + src_buf = v4l2_m2m_buf_remove(&ctx->fh.m2m_ctx->out_q_ctx); + if (src_buf) { + m2m = container_of(src_buf, struct v4l2_m2m_buffer, vb); + src_m2m_buf = container_of(m2m, struct m2m_mmal_buffer, + m2m); + vb2_to_mmal_buffer(src_m2m_buf, src_buf); + + ret = vchiq_mmal_submit_buffer(dev->instance, + &ctx->component->input[0], + &src_m2m_buf->mmal); + v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, + "%s: Submitted ip buffer len %lu, pts %llu, flags %04x\n", + __func__, src_m2m_buf->mmal.length, + src_m2m_buf->mmal.pts, + src_m2m_buf->mmal.mmal_flags); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, + "%s: Failed submitting ip buffer\n", + __func__); + } } - dst_buf = v4l2_m2m_buf_remove(&ctx->fh.m2m_ctx->cap_q_ctx); - if (dst_buf) { - m2m = container_of(dst_buf, struct v4l2_m2m_buffer, vb); - dst_m2m_buf = container_of(m2m, struct m2m_mmal_buffer, m2m); - vb2_to_mmal_buffer(dst_m2m_buf, dst_buf); + if (ctx->fh.m2m_ctx->cap_q_ctx.q.streaming) { + dst_buf = v4l2_m2m_buf_remove(&ctx->fh.m2m_ctx->cap_q_ctx); + if (dst_buf) { + m2m = container_of(dst_buf, struct v4l2_m2m_buffer, vb); + dst_m2m_buf = container_of(m2m, struct m2m_mmal_buffer, + m2m); + vb2_to_mmal_buffer(dst_m2m_buf, dst_buf); - ret = vchiq_mmal_submit_buffer(dev->instance, - &ctx->component->output[0], - &dst_m2m_buf->mmal); - if (ret) - v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed submitting op buffer\n", - __func__); + v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, + "%s: Submitted op buffer\n", __func__); + ret = vchiq_mmal_submit_buffer(dev->instance, + &ctx->component->output[0], + &dst_m2m_buf->mmal); + if (ret) + v4l2_err(&ctx->dev->v4l2_dev, + "%s: Failed submitting op buffer\n", + __func__); + } } v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: Submitted src %p, dst %p\n", From 7f381ecd8e726a639ccc0ce13dcf850c071da5a1 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Sep 2021 17:49:41 +0100 Subject: [PATCH 03/11] staging/mmal-vchiq: Rationalise included headers The list of includes was slightly over generic, and wasn't in alphabetical order. Clean it up. Signed-off-by: Dave Stevenson --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 014fbace54cdbf..a322b97e3f652e 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -15,16 +15,15 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include +#include #include #include -#include -#include -#include -#include #include -#include +#include +#include #include "mmal-common.h" #include "mmal-parameters.h" From e69c3b84a1f9ce41ae9a0ea1749592d0dc7f9714 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 16 Sep 2021 16:32:53 +0100 Subject: [PATCH 04/11] staging: bcm2835-codec: Format changed should trigger drain When a format changed event occurs, the spec says that it triggers an implicit drain, and that needs to be signalled via -EPIPE. For BCM2835, the format changed event happens at the point the format change occurs, so no further buffers exist from before the resolution changed point. We therefore signal the last buffer immediately. We don't have a V4L2 available to us at this point, so set the videobuf2 queue last_buffer_dequeued flag directly. Signed-off-by: Dave Stevenson --- .../staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index 4156018dbf7a1b..73afe99268ff24 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -1005,6 +1005,7 @@ static void handle_fmt_changed(struct bcm2835_codec_ctx *ctx, (struct mmal_msg_event_format_changed *)mmal_buf->buffer; struct mmal_parameter_video_interlace_type interlace; int interlace_size = sizeof(interlace); + struct vb2_queue *vq; int ret; v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Format changed: buff size min %u, rec %u, buff num min %u, rec %u\n", @@ -1074,6 +1075,10 @@ static void handle_fmt_changed(struct bcm2835_codec_ctx *ctx, q_data->field = V4L2_FIELD_NONE; } + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vq->streaming) + vq->last_buffer_dequeued = true; + queue_res_chg_event(ctx); } From 8d44805b8df259feef78dce104a4a2a6fd88c198 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 16 Sep 2021 16:39:07 +0100 Subject: [PATCH 05/11] staging: bcm2835-codec: Signal the firmware to stop on all changes The firmware defaults to not stopping video decode if only the pixel aspect ratio or colourspace change. V4L2 requires us to stop decoding on any change, therefore tell the firmware of the desire for this alternate behaviour. Signed-off-by: Dave Stevenson --- .../vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 10 ++++++++++ .../staging/vc04_services/vchiq-mmal/mmal-parameters.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index 73afe99268ff24..6c6e0ba34a0bf1 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -2483,6 +2483,16 @@ static int bcm2835_codec_create_component(struct bcm2835_codec_ctx *ctx) MMAL_PARAMETER_VIDEO_VALIDATE_TIMESTAMPS, &enable, sizeof(enable)); + /* + * Enable firmware option to stop on colourspace and pixel + * aspect ratio changed + */ + enable = 1; + vchiq_mmal_port_parameter_set(dev->instance, + &ctx->component->control, + MMAL_PARAMETER_VIDEO_STOP_ON_PAR_COLOUR_CHANGE, + &enable, + sizeof(enable)); } else if (dev->role == DEINTERLACE) { /* Select the default deinterlace algorithm. */ int half_framerate = 0; diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h index 622508dc6e2758..21087496a48175 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h @@ -682,6 +682,9 @@ enum mmal_parameter_video_type { /**< Take a @ref MMAL_PARAMETER_BOOLEAN_T */ MMAL_PARAMETER_VIDEO_VALIDATE_TIMESTAMPS, + + /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_VIDEO_STOP_ON_PAR_COLOUR_CHANGE, }; /** Valid mirror modes */ From 61de2ce920cd9f9ae5918d187ec7c8dfdfbe5ab3 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Sep 2021 17:54:11 +0100 Subject: [PATCH 06/11] staging/mmal-vchiq: Add module parameter to enable logging. Adds a module parameter "debug" to enable various logging levels. Signed-off-by: Dave Stevenson --- .../vc04_services/vchiq-mmal/mmal-vchiq.c | 156 ++++++++++-------- 1 file changed, 85 insertions(+), 71 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index a322b97e3f652e..12a670bcc53512 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -31,6 +31,17 @@ #include "mmal-msg.h" #include "vc-sm-cma/vc_sm_knl.h" + +#define pr_dbg_lvl(__level, __debug, __fmt, __arg...) \ + do { \ + if (__debug >= (__level)) \ + printk(KERN_DEBUG __fmt, ##__arg); \ + } while (0) + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info (0-3)"); + /* * maximum number of components supported. * This matches the maximum permitted by default on the VPU @@ -381,7 +392,8 @@ buffer_from_host(struct vchiq_mmal_instance *instance, if (!port->enabled) return -EINVAL; - pr_debug("instance:%u buffer:%p\n", instance->service_handle, buf); + pr_dbg_lvl(3, debug, "instance:%u buffer:%p\n", + instance->service_handle, buf); /* get context */ if (!buf->msg_context) { @@ -549,11 +561,11 @@ static void event_to_host_cb(struct vchiq_mmal_instance *instance, msg_context->u.bulk.pts = MMAL_TIME_UNKNOWN; msg_context->u.bulk.cmd = msg->u.event_to_host.cmd; - pr_debug("event component:%u port type:%d num:%d cmd:0x%x length:%d\n", - msg->u.event_to_host.client_component, - msg->u.event_to_host.port_type, - msg->u.event_to_host.port_num, - msg->u.event_to_host.cmd, msg->u.event_to_host.length); + pr_dbg_lvl(3, debug, "event component:%u port type:%d num:%d cmd:0x%x length:%d\n", + msg->u.event_to_host.client_component, + msg->u.event_to_host.port_type, + msg->u.event_to_host.port_num, + msg->u.event_to_host.cmd, msg->u.event_to_host.length); } schedule_work(&msg_context->u.bulk.work); @@ -566,8 +578,8 @@ static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, struct mmal_msg_context *msg_context; u32 handle; - pr_debug("%s: instance:%p msg:%p msg_len:%d\n", - __func__, instance, msg, msg_len); + pr_dbg_lvl(3, debug, "%s: instance:%p msg:%p msg_len:%d\n", + __func__, instance, msg, msg_len); if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { handle = msg->u.buffer_from_host.drvbuf.client_context; @@ -837,42 +849,43 @@ static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, static void dump_port_info(struct vchiq_mmal_port *port) { - pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled); + pr_dbg_lvl(3, debug, "port handle:0x%x enabled:%d\n", port->handle, + port->enabled); - pr_debug("buffer minimum num:%d size:%d align:%d\n", - port->minimum_buffer.num, - port->minimum_buffer.size, port->minimum_buffer.alignment); + pr_dbg_lvl(3, debug, "buffer minimum num:%d size:%d align:%d\n", + port->minimum_buffer.num, + port->minimum_buffer.size, port->minimum_buffer.alignment); - pr_debug("buffer recommended num:%d size:%d align:%d\n", - port->recommended_buffer.num, - port->recommended_buffer.size, - port->recommended_buffer.alignment); + pr_dbg_lvl(3, debug, "buffer recommended num:%d size:%d align:%d\n", + port->recommended_buffer.num, + port->recommended_buffer.size, + port->recommended_buffer.alignment); - pr_debug("buffer current values num:%d size:%d align:%d\n", - port->current_buffer.num, - port->current_buffer.size, port->current_buffer.alignment); + pr_dbg_lvl(3, debug, "buffer current values num:%d size:%d align:%d\n", + port->current_buffer.num, + port->current_buffer.size, port->current_buffer.alignment); - pr_debug("elementary stream: type:%d encoding:0x%x variant:0x%x\n", - port->format.type, - port->format.encoding, port->format.encoding_variant); + pr_dbg_lvl(3, debug, "elementary stream: type:%d encoding:0x%x variant:0x%x\n", + port->format.type, + port->format.encoding, port->format.encoding_variant); - pr_debug(" bitrate:%d flags:0x%x\n", - port->format.bitrate, port->format.flags); + pr_dbg_lvl(3, debug, " bitrate:%d flags:0x%x\n", + port->format.bitrate, port->format.flags); if (port->format.type == MMAL_ES_TYPE_VIDEO) { - pr_debug - ("es video format: width:%d height:%d colourspace:0x%x\n", - port->es.video.width, port->es.video.height, - port->es.video.color_space); - - pr_debug(" : crop xywh %d,%d,%d,%d\n", - port->es.video.crop.x, - port->es.video.crop.y, - port->es.video.crop.width, port->es.video.crop.height); - pr_debug(" : framerate %d/%d aspect %d/%d\n", - port->es.video.frame_rate.num, - port->es.video.frame_rate.den, - port->es.video.par.num, port->es.video.par.den); + pr_dbg_lvl(3, debug, + "es video format: width:%d height:%d colourspace:0x%x\n", + port->es.video.width, port->es.video.height, + port->es.video.color_space); + + pr_dbg_lvl(3, debug, " : crop xywh %d,%d,%d,%d\n", + port->es.video.crop.x, + port->es.video.crop.y, + port->es.video.crop.width, port->es.video.crop.height); + pr_dbg_lvl(3, debug, " : framerate %d/%d aspect %d/%d\n", + port->es.video.frame_rate.num, + port->es.video.frame_rate.den, + port->es.video.par.num, port->es.video.par.den); } } @@ -903,7 +916,7 @@ static int port_info_set(struct vchiq_mmal_instance *instance, struct mmal_msg *rmsg; struct vchiq_header *rmsg_handle; - pr_debug("setting port info port %p\n", port); + pr_dbg_lvl(1, debug, "setting port info port %p\n", port); if (!port) return -1; dump_port_info(port); @@ -946,8 +959,8 @@ static int port_info_set(struct vchiq_mmal_instance *instance, /* return operation status */ ret = -rmsg->u.port_info_get_reply.status; - pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, - port->component->handle, port->handle); + pr_dbg_lvl(1, debug, "%s:result:%d component:0x%x port:%d\n", __func__, + ret, port->component->handle, port->handle); release_msg: vchiq_release_message(instance->service_handle, rmsg_handle); @@ -1037,13 +1050,13 @@ static int port_info_get(struct vchiq_mmal_instance *instance, rmsg->u.port_info_get_reply.extradata, port->format.extradata_size); - pr_debug("received port info\n"); + pr_dbg_lvl(1, debug, "received port info\n"); dump_port_info(port); release_msg: - pr_debug("%s:result:%d component:0x%x port:%d\n", - __func__, ret, port->component->handle, port->handle); + pr_dbg_lvl(1, debug, "%s:result:%d component:0x%x port:%d\n", + __func__, ret, port->component->handle, port->handle); vchiq_release_message(instance->service_handle, rmsg_handle); @@ -1088,9 +1101,9 @@ static int create_component(struct vchiq_mmal_instance *instance, component->outputs = rmsg->u.component_create_reply.output_num; component->clocks = rmsg->u.component_create_reply.clock_num; - pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n", - component->handle, - component->inputs, component->outputs, component->clocks); + pr_dbg_lvl(2, debug, "Component handle:0x%x in:%d out:%d clock:%d\n", + component->handle, + component->inputs, component->outputs, component->clocks); release_msg: vchiq_release_message(instance->service_handle, rmsg_handle); @@ -1259,10 +1272,9 @@ static int port_action_port(struct vchiq_mmal_instance *instance, ret = -rmsg->u.port_action_reply.status; - pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n", - __func__, - ret, port->component->handle, port->handle, - port_action_type_names[action_type], action_type); + pr_dbg_lvl(2, debug, "%s:result:%d component:0x%x port:%d action:%s(%d)\n", + __func__, ret, port->component->handle, port->handle, + port_action_type_names[action_type], action_type); release_msg: vchiq_release_message(instance->service_handle, rmsg_handle); @@ -1306,11 +1318,11 @@ static int port_action_handle(struct vchiq_mmal_instance *instance, ret = -rmsg->u.port_action_reply.status; - pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d) connect component:0x%x connect port:%d\n", - __func__, - ret, port->component->handle, port->handle, - port_action_type_names[action_type], - action_type, connect_component_handle, connect_port_handle); + pr_dbg_lvl(2, debug, + "%s:result:%d component:0x%x port:%d action:%s(%d) connect component:0x%x connect port:%d\n", + __func__, ret, port->component->handle, port->handle, + port_action_type_names[action_type], + action_type, connect_component_handle, connect_port_handle); release_msg: vchiq_release_message(instance->service_handle, rmsg_handle); @@ -1349,9 +1361,9 @@ static int port_parameter_set(struct vchiq_mmal_instance *instance, ret = -rmsg->u.port_parameter_set_reply.status; - pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", - __func__, - ret, port->component->handle, port->handle, parameter_id); + pr_dbg_lvl(1, debug, "%s:result:%d component:0x%x port:%d parameter:%d\n", + __func__, ret, port->component->handle, port->handle, + parameter_id); release_msg: vchiq_release_message(instance->service_handle, rmsg_handle); @@ -1409,8 +1421,9 @@ static int port_parameter_get(struct vchiq_mmal_instance *instance, /* Always report the size of the returned parameter to the caller */ *value_size = rmsg->u.port_parameter_get_reply.size; - pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, - ret, port->component->handle, port->handle, parameter_id); + pr_dbg_lvl(1, debug, "%s:result:%d component:0x%x port:%d parameter:%d\n", + __func__, ret, port->component->handle, port->handle, + parameter_id); release_msg: vchiq_release_message(instance->service_handle, rmsg_handle); @@ -1667,7 +1680,7 @@ int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, if (!dst) { /* do not make new connection */ ret = 0; - pr_debug("not making new connection\n"); + pr_dbg_lvl(3, debug, "not making new connection\n"); goto release_unlock; } @@ -1685,14 +1698,14 @@ int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, /* set new format */ ret = port_info_set(instance, dst); if (ret) { - pr_debug("setting port info failed\n"); + pr_dbg_lvl(1, debug, "setting port info failed\n"); goto release_unlock; } /* read what has actually been set */ ret = port_info_get(instance, dst); if (ret) { - pr_debug("read back port info failed\n"); + pr_dbg_lvl(1, debug, "read back port info failed\n"); goto release_unlock; } @@ -1701,9 +1714,9 @@ int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, MMAL_MSG_PORT_ACTION_TYPE_CONNECT, dst->component->handle, dst->handle); if (ret < 0) { - pr_debug("connecting port %d:%d to %d:%d failed\n", - src->component->handle, src->handle, - dst->component->handle, dst->handle); + pr_dbg_lvl(2, debug, "connecting port %d:%d to %d:%d failed\n", + src->component->handle, src->handle, + dst->component->handle, dst->handle); goto release_unlock; } src->connected = dst; @@ -1728,7 +1741,8 @@ int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, * videobuf2 won't let us have the dmabuf there. */ if (port->zero_copy && buffer->dma_buf && !buffer->vcsm_handle) { - pr_debug("%s: import dmabuf %p\n", __func__, buffer->dma_buf); + pr_dbg_lvl(2, debug, "%s: import dmabuf %p\n", + __func__, buffer->dma_buf); ret = vc_sm_cma_import_dmabuf(buffer->dma_buf, &buffer->vcsm_handle); if (ret) { @@ -1744,8 +1758,8 @@ int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, vc_sm_cma_free(buffer->vcsm_handle); return ret; } - pr_debug("%s: import dmabuf %p - got vc handle %08X\n", - __func__, buffer->dma_buf, buffer->vc_handle); + pr_dbg_lvl(2, debug, "%s: import dmabuf %p - got vc handle %08X\n", + __func__, buffer->dma_buf, buffer->vc_handle); } ret = buffer_from_host(instance, port, buffer); @@ -1784,8 +1798,8 @@ int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) if (buf->vcsm_handle) { int ret; - pr_debug("%s: vc_sm_cma_free on handle %p\n", __func__, - buf->vcsm_handle); + pr_dbg_lvl(2, debug, "%s: vc_sm_cma_free on handle %p\n", __func__, + buf->vcsm_handle); ret = vc_sm_cma_free(buf->vcsm_handle); if (ret) pr_err("%s: vcsm_free failed, ret %d\n", __func__, ret); From bbce3763f413526daeb371a8b33893ab00f60b71 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 16 Sep 2021 16:46:58 +0100 Subject: [PATCH 07/11] staging: bcm2835-codec: Queue flushed buffers instead of completing When a buffer is returned on a port that is disabled, return it to the videobuf2 QUEUED state instead of DONE which returns it to the client. Signed-off-by: Dave Stevenson --- .../staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index 6c6e0ba34a0bf1..03c9ccb35ee1a4 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -926,7 +926,9 @@ static void ip_buffer_cb(struct vchiq_mmal_instance *instance, v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: no error. Return buffer %p\n", __func__, &buf->m2m.vb.vb2_buf); - vb2_buffer_done(&buf->m2m.vb.vb2_buf, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->m2m.vb.vb2_buf, + port->enabled ? VB2_BUF_STATE_DONE : + VB2_BUF_STATE_QUEUED); ctx->num_ip_buffers++; v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: done %d input buffers\n", From 758760bae3fa5695ab07c11c61a4411488422453 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 21 Sep 2021 17:17:57 +0100 Subject: [PATCH 08/11] staging: mmal-vchiq: Reset buffers_with_vpu on port_enable Should we go through the timeout failure case with port_disable not returning all buffers for whatever reason, the buffers_with_vpu counter gets left at a non-zero value, which will cause reference counting issues should the instance be reused. Reset the count when the port is enabled again, but before any buffers have been sent to the VPU. Signed-off-by: Dave Stevenson --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 12a670bcc53512..a6cdc7dc9c300e 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -1500,6 +1500,8 @@ static int port_enable(struct vchiq_mmal_instance *instance, port->enabled = 1; + atomic_set(&port->buffers_with_vpu, 0); + if (port->buffer_cb) { /* send buffer headers to videocore */ hdr_count = 1; From 28f673ba2710bc16fc6dfd66e50d813bd87579c2 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 22 Sep 2021 16:42:49 +0100 Subject: [PATCH 09/11] staging: bcm2835_codec: Correct flushing code for refcounting Completions don't reference count, so setting the completion on the first buffer returned and then not reinitialising it means that the flush function doesn't behave as intended. Signal the completion when the last buffer is returned. Signed-off-by: Dave Stevenson --- .../bcm2835-codec/bcm2835-v4l2-codec.c | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index 03c9ccb35ee1a4..1b8611549f4f89 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -934,7 +934,7 @@ static void ip_buffer_cb(struct vchiq_mmal_instance *instance, v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: done %d input buffers\n", __func__, ctx->num_ip_buffers); - if (!port->enabled) + if (!port->enabled && atomic_read(&port->buffers_with_vpu)) complete(&ctx->frame_cmplt); } @@ -1135,7 +1135,8 @@ static void op_buffer_cb(struct vchiq_mmal_instance *instance, __func__, mmal_buf->mmal_flags); if (!(mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS)) { vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_QUEUED); - if (!port->enabled) + if (!port->enabled && + atomic_read(&port->buffers_with_vpu)) complete(&ctx->frame_cmplt); return; } @@ -1178,7 +1179,7 @@ static void op_buffer_cb(struct vchiq_mmal_instance *instance, v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: done %d output buffers\n", __func__, ctx->num_op_buffers); - if (!port->enabled) + if (!port->enabled && atomic_read(&port->buffers_with_vpu)) complete(&ctx->frame_cmplt); } @@ -1596,6 +1597,8 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, return 0; if (port->enabled) { + unsigned int num_buffers; + /* * This should only ever happen with DECODE and the MMAL output * port that has been enabled for resolution changed events. @@ -1606,10 +1609,18 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || atomic_read(&port->buffers_with_vpu)); + /* + * Disable will reread the port format, so retain buffer count. + */ + num_buffers = port->current_buffer.num; + ret = vchiq_mmal_port_disable(ctx->dev->instance, port); if (ret) v4l2_err(&ctx->dev->v4l2_dev, "%s: Error disabling port update buffer count, ret %d\n", __func__, ret); + + port->current_buffer.num = num_buffers; + reenable_port = true; } @@ -2787,7 +2798,7 @@ static void bcm2835_codec_flush_buffers(struct bcm2835_codec_ctx *ctx, { int ret; - while (atomic_read(&port->buffers_with_vpu)) { + if (atomic_read(&port->buffers_with_vpu)) { v4l2_dbg(1, debug, &ctx->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(&ctx->frame_cmplt, @@ -2796,7 +2807,6 @@ static void bcm2835_codec_flush_buffers(struct bcm2835_codec_ctx *ctx, v4l2_err(&ctx->dev->v4l2_dev, "%s: Timeout waiting for buffers to be returned - %d outstanding\n", __func__, atomic_read(&port->buffers_with_vpu)); - break; } } } From a7028801f2c233dfd2b09f28e43d50d0c4e5d2df Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 20 Sep 2021 15:00:51 +0100 Subject: [PATCH 10/11] staging: bcm2835-codec: Ensure all ctrls are set on streamon Currently the code was only setting some controls from bcm2835_codec_set_ctrls, but it's simpler to use v4l2_ctrl_handler_setup to avoid forgetting to adding new controls to the list. Signed-off-by: Dave Stevenson --- .../bcm2835-codec/bcm2835-v4l2-codec.c | 51 +++++++------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index 1b8611549f4f89..f5eaff3e461232 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -2437,33 +2437,6 @@ static const struct v4l2_ioctl_ops bcm2835_codec_ioctl_ops = { .vidioc_enum_framesizes = vidioc_enum_framesizes, }; -static int bcm2835_codec_set_ctrls(struct bcm2835_codec_ctx *ctx) -{ - /* - * Query the control handler for the value of the various controls and - * set them. - */ - const u32 control_ids[] = { - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, - V4L2_CID_MPEG_VIDEO_HEADER_MODE, - V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, - V4L2_CID_MPEG_VIDEO_H264_LEVEL, - V4L2_CID_MPEG_VIDEO_H264_PROFILE, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(control_ids); i++) { - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&ctx->hdl, control_ids[i]); - if (ctrl) - bcm2835_codec_s_ctrl(ctrl); - } - - return 0; -} - static int bcm2835_codec_create_component(struct bcm2835_codec_ctx *ctx) { struct bcm2835_codec_dev *dev = ctx->dev; @@ -2567,9 +2540,6 @@ static int bcm2835_codec_create_component(struct bcm2835_codec_ctx *ctx) ctx->q_data[V4L2_M2M_SRC].sizeimage, ctx->component->output[0].minimum_buffer.size); - /* Now we have a component we can set all the ctrls */ - bcm2835_codec_set_ctrls(ctx); - /* Enable SPS Timing header so framerate information is encoded * in the H264 header. */ @@ -2598,6 +2568,10 @@ static int bcm2835_codec_create_component(struct bcm2835_codec_ctx *ctx) ctx->q_data[V4L2_M2M_DST].sizeimage, ctx->component->output[0].minimum_buffer.size); } + + /* Now we have a component we can set all the ctrls */ + ret = v4l2_ctrl_handler_setup(&ctx->hdl); + v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: component created as %s\n", __func__, components[dev->role]); @@ -3099,7 +3073,9 @@ static int bcm2835_codec_open(struct file *file) file->private_data = &ctx->fh; ctx->dev = dev; hdl = &ctx->hdl; - if (dev->role == ENCODE) { + switch (dev->role) { + case ENCODE: + { /* Encode controls */ v4l2_ctrl_handler_init(hdl, 9); @@ -3158,7 +3134,10 @@ static int bcm2835_codec_open(struct file *file) } ctx->fh.ctrl_handler = hdl; v4l2_ctrl_handler_setup(hdl); - } else if (dev->role == DECODE) { + } + break; + case DECODE: + { v4l2_ctrl_handler_init(hdl, 1); v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops, @@ -3171,6 +3150,14 @@ static int bcm2835_codec_open(struct file *file) ctx->fh.ctrl_handler = hdl; v4l2_ctrl_handler_setup(hdl); } + break; + case ISP: + case DEINTERLACE: + { + v4l2_ctrl_handler_init(hdl, 0); + } + break; + } ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); From 3da0a0983ec36b4ce6ef12fa46c92e8ab55a2c6f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 20 Sep 2021 14:37:17 +0100 Subject: [PATCH 11/11] staging: bcm2835-codec: Add support for H&V Flips to ISP The ISP can do H & V flips whilst resizing or converting the image, so expose that via V4L2_CID_[H|V]FLIP. Signed-off-by: Dave Stevenson --- .../bcm2835-codec/bcm2835-v4l2-codec.c | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index f5eaff3e461232..6078d6e2ace019 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -679,6 +679,9 @@ struct bcm2835_codec_ctx { enum v4l2_xfer_func xfer_func; enum v4l2_quantization quant; + int hflip; + int vflip; + /* Source and destination queue data */ struct bcm2835_codec_q_data q_data[2]; s32 bitrate; @@ -2202,6 +2205,34 @@ static int bcm2835_codec_s_ctrl(struct v4l2_ctrl *ctrl) sizeof(mmal_bool)); break; } + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: { + u32 u32_value; + + if (ctrl->id == V4L2_CID_HFLIP) + ctx->hflip = ctrl->val; + else + ctx->vflip = ctrl->val; + + if (!ctx->component) + break; + + if (ctx->hflip && ctx->vflip) + u32_value = MMAL_PARAM_MIRROR_BOTH; + else if (ctx->hflip) + u32_value = MMAL_PARAM_MIRROR_HORIZONTAL; + else if (ctx->vflip) + u32_value = MMAL_PARAM_MIRROR_VERTICAL; + else + u32_value = MMAL_PARAM_MIRROR_NONE; + + ret = vchiq_mmal_port_parameter_set(ctx->dev->instance, + &ctx->component->input[0], + MMAL_PARAMETER_MIRROR, + &u32_value, + sizeof(u32_value)); + break; + } default: v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); @@ -3152,6 +3183,23 @@ static int bcm2835_codec_open(struct file *file) } break; case ISP: + { + v4l2_ctrl_handler_init(hdl, 2); + + v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops, + V4L2_CID_HFLIP, + 1, 0, 1, 0); + v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops, + V4L2_CID_VFLIP, + 1, 0, 1, 0); + if (hdl->error) { + rc = hdl->error; + goto free_ctrl_handler; + } + ctx->fh.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + } + break; case DEINTERLACE: { v4l2_ctrl_handler_init(hdl, 0);