From 6eded7dd8bcf00c6c4f354cd2edfaabc8e82e8b7 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Fri, 9 Oct 2020 10:40:27 +0100
Subject: [PATCH] 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 <dave.stevenson@raspberrypi.com>
---
 .../bcm2835-codec/bcm2835-v4l2-codec.c        | 137 +++++++++++++++---
 1 file changed, 117 insertions(+), 20 deletions(-)

--- 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_c
 	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_c
 	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_c
 			 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_componen
 
 	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
 	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 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
 		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
 				 __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
 				      &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
 			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
 			 __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 */
